Skip to content

Commit 559e949

Browse files
committed
Fix cleanup plan lint after merge
2 parents 2c4b699 + 8ce5ebc commit 559e949

18 files changed

Lines changed: 1053 additions & 288 deletions

inc/Abilities/WorkspaceAbilities.php

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,29 +1562,33 @@ private function registerAbilities(): void {
15621562
'input_schema' => array(
15631563
'type' => 'object',
15641564
'properties' => array(
1565-
'mode' => array(
1565+
'mode' => array(
15661566
'type' => 'string',
1567-
'description' => 'Cleanup mode: inventory, artifacts, retention, or emergency.',
1567+
'description' => 'Cleanup mode: inventory, artifacts, retention, stale-worktrees, or emergency.',
15681568
),
1569-
'force' => array(
1569+
'force' => array(
15701570
'type' => 'boolean',
15711571
'description' => 'Forward force=true to cleanup tasks that support it.',
15721572
),
1573-
'dry_run' => array(
1573+
'dry_run' => array(
15741574
'type' => 'boolean',
15751575
'description' => 'Rejected for background cleanup scheduling; use review abilities for dry-runs.',
15761576
),
1577-
'older_than' => array(
1577+
'older_than' => array(
15781578
'type' => 'string',
15791579
'description' => 'Optional worktree retention age gate such as 14d.',
15801580
),
1581-
'source' => array(
1581+
'worktree_stale_only' => array(
1582+
'type' => 'boolean',
1583+
'description' => 'Only plan stale/inactive worktrees for destructive removal.',
1584+
),
1585+
'source' => array(
15821586
'type' => 'string',
15831587
'description' => 'Caller source marker.',
15841588
),
1585-
'user_id' => array( 'type' => 'integer' ),
1586-
'agent_id' => array( 'type' => 'integer' ),
1587-
'agent_slug' => array( 'type' => 'string' ),
1589+
'user_id' => array( 'type' => 'integer' ),
1590+
'agent_id' => array( 'type' => 'integer' ),
1591+
'agent_slug' => array( 'type' => 'string' ),
15881592
),
15891593
),
15901594
'output_schema' => array(
@@ -2327,18 +2331,20 @@ private function registerAbilities(): void {
23272331
'force_artifact_cleanup' => array( 'type' => 'boolean' ),
23282332
'worktree_older_than' => array( 'type' => 'string' ),
23292333
'worktree_sort' => array( 'type' => 'string' ),
2334+
'worktree_stale_only' => array( 'type' => 'boolean' ),
23302335
'plan' => array( 'type' => 'object' ),
23312336
),
23322337
),
23332338
'output_schema' => array(
23342339
'type' => 'object',
23352340
'properties' => array(
2336-
'success' => array( 'type' => 'boolean' ),
2337-
'mode' => array( 'type' => 'string' ),
2338-
'plan_id' => array( 'type' => 'string' ),
2339-
'rows' => array( 'type' => 'object' ),
2340-
'chunks' => array( 'type' => 'array' ),
2341-
'summary' => array( 'type' => 'object' ),
2341+
'success' => array( 'type' => 'boolean' ),
2342+
'mode' => array( 'type' => 'string' ),
2343+
'plan_id' => array( 'type' => 'string' ),
2344+
'rows' => array( 'type' => 'object' ),
2345+
'action_rows' => array( 'type' => 'object' ),
2346+
'chunks' => array( 'type' => 'array' ),
2347+
'summary' => array( 'type' => 'object' ),
23422348
),
23432349
),
23442350
'execute_callback' => array( self::class, 'workspaceCleanupPlan' ),
@@ -2493,14 +2499,21 @@ public static function getCapabilities( array $input ): array { // phpcs:ignor
24932499
* @return array Result.
24942500
*/
24952501
public static function showRepo( array $input ): array|\WP_Error {
2502+
$workspace = new Workspace();
2503+
if ( RemoteWorkspaceBackend::should_handle() ) {
2504+
$local_result = self::showLocalWorkspaceHandleIfPresent($workspace, (string) ( $input['name'] ?? '' ));
2505+
if ( null !== $local_result ) {
2506+
return $local_result;
2507+
}
2508+
}
2509+
24962510
if ( RemoteWorkspaceBackend::should_handle() ) {
24972511
$result = ( new RemoteWorkspaceBackend() )->show($input['name'] ?? '');
24982512
if ( ! self::shouldFallbackToLocalWorkspace($result) ) {
24992513
return $result;
25002514
}
25012515
}
25022516

2503-
$workspace = new Workspace();
25042517
return $workspace->show_repo($input['name'] ?? '');
25052518
}
25062519

@@ -3461,17 +3474,34 @@ public static function worktreeAdd( array $input ): array|\WP_Error {
34613474
* Whether a repo argument resolves to an editable local primary checkout.
34623475
*/
34633476
private static function hasLocalPrimaryCheckout( Workspace $workspace, string $repo ): bool {
3464-
if ( '' === trim($repo) ) {
3477+
$result = self::showLocalWorkspaceHandleIfPresent($workspace, $repo);
3478+
if ( null === $result ) {
34653479
return false;
34663480
}
34673481

3468-
$result = $workspace->show_repo($repo);
3482+
$path = (string) ( $result['path'] ?? '' );
3483+
return ! str_contains(basename($path), '@');
3484+
}
3485+
3486+
/**
3487+
* Return local workspace details for an existing local handle, if present.
3488+
*/
3489+
private static function showLocalWorkspaceHandleIfPresent( Workspace $workspace, string $handle ): ?array {
3490+
if ( '' === trim($handle) ) {
3491+
return null;
3492+
}
3493+
3494+
$result = $workspace->show_repo($handle);
34693495
if ( is_wp_error($result) ) {
3470-
return false;
3496+
return null;
34713497
}
34723498

34733499
$path = (string) ( $result['path'] ?? '' );
3474-
return '' !== $path && ! str_starts_with($path, 'github://') && ! str_contains(basename($path), '@');
3500+
if ( '' === $path || str_starts_with($path, 'github://') ) {
3501+
return null;
3502+
}
3503+
3504+
return $result;
34753505
}
34763506

34773507
/**
@@ -3585,7 +3615,7 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
35853615

35863616
$mode = strtolower(preg_replace('/[^a-z0-9_\-]/', '', (string) ( $input['mode'] ?? 'retention' )));
35873617
$map = array(
3588-
'inventory' => array(
3618+
'inventory' => array(
35893619
'task_type' => 'workspace_hygiene_report',
35903620
'params' => array(
35913621
'include_cleanup' => true,
@@ -3594,7 +3624,7 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
35943624
'size_limit' => 200,
35953625
),
35963626
),
3597-
'artifacts' => array(
3627+
'artifacts' => array(
35983628
'task_type' => 'workspace_retention_cleanup',
35993629
'params' => array(
36003630
'dry_run' => false,
@@ -3603,7 +3633,18 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
36033633
'skip_github' => true,
36043634
),
36053635
),
3606-
'retention' => array(
3636+
'stale-worktrees' => array(
3637+
'task_type' => 'workspace_retention_cleanup',
3638+
'params' => array(
3639+
'dry_run' => false,
3640+
'artifact_cleanup' => false,
3641+
'worktree_cleanup' => true,
3642+
'skip_github' => true,
3643+
'worktree_older_than' => '14d',
3644+
'worktree_stale_only' => true,
3645+
),
3646+
),
3647+
'retention' => array(
36073648
'task_type' => 'workspace_retention_cleanup',
36083649
'params' => array(
36093650
'dry_run' => false,
@@ -3613,7 +3654,7 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
36133654
'worktree_older_than' => '14d',
36143655
),
36153656
),
3616-
'emergency' => array(
3657+
'emergency' => array(
36173658
'task_type' => 'workspace_disk_emergency_cleanup',
36183659
'params' => array(
36193660
'artifact_chunk_size' => 10,
@@ -3639,6 +3680,9 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
36393680
if ( isset($input['older_than']) && '' !== trim( (string) $input['older_than']) ) {
36403681
$params['worktree_older_than'] = trim( (string) $input['older_than']);
36413682
}
3683+
if ( isset($input['worktree_stale_only']) ) {
3684+
$params['worktree_stale_only'] = (bool) $input['worktree_stale_only'];
3685+
}
36423686
if ( 'artifacts' === $mode ) {
36433687
if ( isset($input['limit']) ) {
36443688
$params['limit'] = (int) $input['limit'];
@@ -4034,6 +4078,7 @@ public static function workspaceCleanupPlan( array $input ): array|\WP_Error {
40344078
'force_artifact_cleanup' => ! empty($input['force_artifact_cleanup']),
40354079
'include_resolvers' => ! empty($input['include_resolvers']),
40364080
'mode' => (string) ( $input['mode'] ?? 'cleanup_plan' ),
4081+
'worktree_stale_only' => ! empty($input['worktree_stale_only']),
40374082
);
40384083
foreach ( array( 'include_artifacts', 'include_worktrees' ) as $key ) {
40394084
if ( array_key_exists($key, $input) ) {

inc/Cleanup/DataMachineJobCleanupRunEvidenceStore.php

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,17 @@ public function read( string $run_id, bool $include_evidence = false, bool $incl
3737
$child_jobs = $this->get_cleanup_run_descendant_jobs($job_id);
3838
$aggregate = $this->aggregate_cleanup_child_jobs($child_jobs);
3939
$children = $aggregate['children'];
40-
$state = $this->cleanup_run_state( (string) ( $job['status'] ?? '' ), $children);
40+
$parent_status = (string) ( $job['status'] ?? '' );
41+
$state = $this->cleanup_run_state($parent_status, $children, $parent_result, $aggregate);
4142

4243
$children_for_output = ( $include_evidence || $include_details ) ? $children : $this->summarize_cleanup_children($children);
4344
$output = array(
4445
'success' => true,
4546
'state' => $state,
4647
'run_id' => $this->cleanup_run_id($job_id),
4748
'job_id' => $job_id,
48-
'status' => in_array($state, array( 'children_processing', 'partial_failed' ), true) ? $state : ( $job['status'] ?? '' ),
49+
'status' => $state,
50+
'parent_status' => $parent_status,
4951
'created_at' => $job['created_at'] ?? '',
5052
'parent_completed_at' => $job['completed_at'] ?? '',
5153
'artifact_cleanup' => $aggregate['artifact_cleanup'],
@@ -132,6 +134,7 @@ private function aggregate_cleanup_child_jobs( array $child_jobs ): array {
132134
'processing' => 0,
133135
'completed' => 0,
134136
'failed' => 0,
137+
'skipped' => 0,
135138
'running' => 0,
136139
'total' => 0,
137140
'statuses' => array(),
@@ -218,6 +221,7 @@ private function summarize_cleanup_children( array $children ): array {
218221
'processing' => (int) ( $children['processing'] ?? 0 ),
219222
'completed' => (int) ( $children['completed'] ?? 0 ),
220223
'failed' => (int) ( $children['failed'] ?? 0 ),
224+
'skipped' => (int) ( $children['skipped'] ?? 0 ),
221225
'running' => (int) ( $children['running'] ?? 0 ),
222226
'total' => (int) ( $children['total'] ?? 0 ),
223227
'statuses' => (array) ( $children['statuses'] ?? array() ),
@@ -255,9 +259,9 @@ private function cleanup_run_drain_summary( int $job_id, string $state, array $c
255259
)
256260
)
257261
);
258-
$run_id = $this->cleanup_run_id($job_id);
259-
$cleanup_items = (array) ( $aggregate['cleanup_items'] ?? array() );
260-
$commands = array(
262+
$run_id = $this->cleanup_run_id($job_id);
263+
$cleanup_items = (array) ( $aggregate['cleanup_items'] ?? array() );
264+
$commands = array(
261265
'parent' => sprintf('studio wp datamachine drain --job-id=%d', $job_id),
262266
'verify' => sprintf('studio wp datamachine-code workspace cleanup status %s --format=json', $run_id),
263267
);
@@ -266,7 +270,7 @@ private function cleanup_run_drain_summary( int $job_id, string $state, array $c
266270
}
267271

268272
return array(
269-
'needed' => in_array($state, array( 'running', 'children_processing' ), true),
273+
'needed' => in_array($state, array( 'running', 'waiting_on_children' ), true),
270274
'commands' => $commands,
271275
'active_child_job_ids' => $active_child_ids,
272276
'bytes_reclaimed' => (int) ( $cleanup_items['bytes_reclaimed'] ?? 0 ),
@@ -478,6 +482,10 @@ private function count_cleanup_child_status( array &$children, string $status ):
478482
++$children['failed'];
479483
return;
480484
}
485+
if ( str_starts_with($status, 'skipped') ) {
486+
++$children['skipped'];
487+
return;
488+
}
481489
if ( str_starts_with($status, 'completed') ) {
482490
++$children['completed'];
483491
}
@@ -546,9 +554,6 @@ private function merge_cleanup_reason_examples( array &$examples, array $rows ):
546554
private function sum_cleanup_rows_bytes( array $rows, array $fields ): int {
547555
$total = 0;
548556
foreach ( $rows as $row ) {
549-
if ( ! is_array($row) ) {
550-
continue;
551-
}
552557
$found = false;
553558
foreach ( $fields as $field ) {
554559
if ( isset($row[ $field ]) ) {
@@ -574,22 +579,69 @@ private function sum_cleanup_rows_bytes( array $rows, array $fields ): int {
574579
* @param array $children Child summary.
575580
* @return string
576581
*/
577-
private function cleanup_run_state( string $status, array $children ): string {
582+
private function cleanup_run_state( string $status, array $children, array $parent_result, array $aggregate ): string {
578583
$parent_state = $this->cleanup_job_state($status);
579-
if ( in_array($parent_state, array( 'cancelled', 'partial_failure' ), true) ) {
584+
if ( in_array($parent_state, array( 'cancelled', 'failed' ), true) ) {
580585
return $parent_state;
581586
}
582587

583588
if ( (int) ( $children['running'] ?? 0 ) > 0 ) {
584-
return 'children_processing';
589+
return 'waiting_on_children';
585590
}
586591
if ( (int) ( $children['failed'] ?? 0 ) > 0 ) {
587-
return 'partial_failed';
592+
return 'failed';
593+
}
594+
595+
$child_total = (int) ( $children['total'] ?? 0 );
596+
$terminal_child = (int) ( $children['completed'] ?? 0 ) + (int) ( $children['skipped'] ?? 0 );
597+
if ( $child_total > 0 && $terminal_child >= $child_total ) {
598+
return 'complete';
599+
}
600+
601+
if ( 0 === $child_total && $this->cleanup_parent_has_no_planned_work($parent_result, $aggregate) ) {
602+
return 'no_work';
588603
}
589604

590605
return $parent_state;
591606
}
592607

608+
/**
609+
* Determine whether parent cleanup evidence proves there was no work to schedule.
610+
*
611+
* @param array $parent_result Parent system task result.
612+
* @param array $aggregate Child aggregate.
613+
* @return bool
614+
*/
615+
private function cleanup_parent_has_no_planned_work( array $parent_result, array $aggregate ): bool {
616+
if ( array() === $parent_result ) {
617+
return false;
618+
}
619+
620+
$cleanup_items = (array) ( $aggregate['cleanup_items'] ?? array() );
621+
if ( (int) ( $cleanup_items['planned_rows'] ?? 0 ) > 0 ) {
622+
return false;
623+
}
624+
625+
if ( array_key_exists('chunks', $parent_result) && array() !== (array) $parent_result['chunks'] ) {
626+
return false;
627+
}
628+
629+
if ( isset($parent_result['evidence']) && is_array($parent_result['evidence']) && (int) ( $parent_result['evidence']['planned_chunks'] ?? 0 ) > 0 ) {
630+
return false;
631+
}
632+
633+
if ( isset($parent_result['chunk_row_counts']) && is_array($parent_result['chunk_row_counts']) ) {
634+
foreach ( $parent_result['chunk_row_counts'] as $count ) {
635+
if ( (int) $count > 0 ) {
636+
return false;
637+
}
638+
}
639+
return true;
640+
}
641+
642+
return array_key_exists('chunks', $parent_result) || array_key_exists('chunk_row_counts', $parent_result);
643+
}
644+
593645
/**
594646
* Convert job status into cleanup run state.
595647
*
@@ -604,7 +656,7 @@ private function cleanup_job_state( string $status ): string {
604656
return 'cancelled';
605657
}
606658
if ( str_starts_with($status, 'failed') ) {
607-
return 'partial_failure';
659+
return 'failed';
608660
}
609661
if ( str_starts_with($status, 'completed') ) {
610662
return 'complete';

0 commit comments

Comments
 (0)