Skip to content

Commit bc99891

Browse files
authored
Merge pull request #814 from Extra-Chill/feat/active-no-signal-safe-drain-811
Add active no-signal cleanup drain
2 parents 8f7b548 + ecd01b3 commit bc99891

4 files changed

Lines changed: 233 additions & 30 deletions

File tree

inc/Abilities/WorkspaceAbilities.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,65 @@ private function registerAbilities(): void {
21872187
)
21882188
);
21892189

2190+
AbilityRegistry::register(
2191+
'datamachine-code/workspace-worktree-active-no-signal-drain',
2192+
array(
2193+
'label' => 'Drain Safe Active Worktree Cleanup',
2194+
'description' => 'Run safe active/no-signal classifiers, remove newly cleanup-eligible worktrees through bounded cleanup, and stop with protected blockers. Refuses force and unpushed discard.',
2195+
'category' => 'datamachine-code-workspace',
2196+
'input_schema' => array(
2197+
'type' => 'object',
2198+
'properties' => array(
2199+
'apply' => array(
2200+
'type' => 'boolean',
2201+
'description' => 'If true, write safe cleanup metadata and remove bounded cleanup-eligible worktrees. Defaults to preview mode.',
2202+
),
2203+
'limit' => array(
2204+
'type' => 'integer',
2205+
'description' => 'Page/removal limit, clamped to 1..1000. Defaults to 100.',
2206+
),
2207+
'passes' => array(
2208+
'type' => 'integer',
2209+
'description' => 'Maximum apply passes, clamped to 1..25. Preview mode runs one pass.',
2210+
),
2211+
'offset' => array(
2212+
'type' => 'integer',
2213+
'description' => 'Stage pagination offset for resumed runs.',
2214+
),
2215+
'stage' => array(
2216+
'type' => 'string',
2217+
'enum' => array( 'finalized', 'equivalent-clean', 'merged', 'remote-clean', 'bounded' ),
2218+
'description' => 'Active/no-signal stage to start from. Defaults to finalized.',
2219+
),
2220+
'until_budget' => array(
2221+
'type' => 'string',
2222+
'description' => 'Optional compact wall-clock budget such as 60s, 10m, or 1h.',
2223+
),
2224+
'source' => array(
2225+
'type' => 'string',
2226+
'description' => 'Caller source marker forwarded to underlying cleanup abilities.',
2227+
),
2228+
),
2229+
),
2230+
'output_schema' => array(
2231+
'type' => 'object',
2232+
'properties' => array(
2233+
'success' => array( 'type' => 'boolean' ),
2234+
'mode' => array( 'type' => 'string' ),
2235+
'applied' => array( 'type' => 'boolean' ),
2236+
'summary' => array( 'type' => 'object' ),
2237+
'steps' => array( 'type' => 'object' ),
2238+
'blocked' => array( 'type' => 'array' ),
2239+
'continuation' => array( 'type' => 'object' ),
2240+
'next_commands' => array( 'type' => 'array' ),
2241+
),
2242+
),
2243+
'execute_callback' => array( self::class, 'worktreeActiveNoSignalDrain' ),
2244+
'permission_callback' => fn() => PermissionHelper::can_manage(),
2245+
'meta' => array( 'show_in_rest' => false ),
2246+
)
2247+
);
2248+
21902249
AbilityRegistry::register(
21912250
'datamachine-code/workspace-worktree-abandoned-cleanup',
21922251
array(
@@ -4247,6 +4306,23 @@ public static function worktreeActiveNoSignalRemoteCleanApply( array $input ): a
42474306
return $workspace->worktree_active_no_signal_remote_clean_apply($opts);
42484307
}
42494308

4309+
/**
4310+
* Drain safe active/no-signal cleanup classifications and removals.
4311+
*
4312+
* @param array $input Orchestration input.
4313+
* @return array<string,mixed>|\WP_Error
4314+
*/
4315+
public static function worktreeActiveNoSignalDrain( array $input ): array|\WP_Error {
4316+
$input['active_no_signal_drain'] = true;
4317+
if ( ! array_key_exists('force', $input) ) {
4318+
$input['force'] = false;
4319+
}
4320+
4321+
$orchestrator = new WorkspaceAbandonedCleanupOrchestrator();
4322+
4323+
return $orchestrator->run($input);
4324+
}
4325+
42504326
/**
42514327
* Orchestrate abandoned-worktree cleanup across existing bounded abilities.
42524328
*

inc/Cli/Commands/WorkspaceCommand.php

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class WorkspaceCommand extends BaseCommand {
7171
'ability' => 'datamachine-code/workspace-worktree-active-no-signal-remote-clean-apply',
7272
'input_builder' => 'build_worktree_active_no_signal_input',
7373
),
74+
'active-no-signal-drain' => array( 'ability' => 'datamachine-code/workspace-worktree-active-no-signal-drain' ),
7475
'refresh-context' => array( 'ability' => 'datamachine-code/workspace-worktree-refresh-context' ),
7576
'finalize' => array( 'ability' => 'datamachine-code/workspace-worktree-finalize' ),
7677
'mark-cleanup-eligible' => array( 'ability' => 'datamachine-code/workspace-worktree-finalize' ),
@@ -3220,6 +3221,7 @@ private function renderGitOperationResult( string $operation, array $result, arr
32203221
* active-no-signal-report, active-no-signal-finalized-apply,
32213222
* active-no-signal-equivalent-clean-apply,
32223223
* active-no-signal-merged-apply, active-no-signal-remote-clean-apply,
3224+
* active-no-signal-drain,
32233225
* refresh-context, finalize, mark-cleanup-eligible.
32243226
*
32253227
* [<repo>]
@@ -3419,13 +3421,15 @@ private function renderGitOperationResult( string $operation, array $result, arr
34193421
* audit.
34203422
*
34213423
* [--passes=<count>]
3422-
* : For `abandoned`, maximum apply passes to run after marking eligible rows.
3424+
* : For `abandoned` and `active-no-signal-drain`, maximum apply passes to run after marking eligible rows.
34233425
* For `cleanup-eligible-drain`, maximum bounded cleanup-eligible apply
34243426
* passes to run. Preview mode always runs one non-destructive pass.
34253427
*
34263428
* [--stage=<stage>]
34273429
* : For `abandoned`, resume from a specific orchestration stage. Supported
34283430
* values: reconcile, finalized, equivalent-clean, merged, remote-clean, bounded.
3431+
* For `active-no-signal-drain`, supported values are finalized,
3432+
* equivalent-clean, merged, remote-clean, and bounded.
34293433
*
34303434
* [--offset=<count>]
34313435
* : For `cleanup --dry-run`, `cleanup-artifacts --dry-run`,
@@ -3543,6 +3547,8 @@ private function renderGitOperationResult( string $operation, array $result, arr
35433547
* # One operator pass for abandoned worktrees: reconcile, mark safe rows, remove eligible rows, and report blockers
35443548
* wp datamachine-code workspace worktree abandoned --format=json
35453549
* wp datamachine-code workspace worktree abandoned --apply --force --limit=100 --passes=5 --until-budget=120s --format=json
3550+
* wp datamachine-code workspace worktree active-no-signal-drain --format=json
3551+
* wp datamachine-code workspace worktree active-no-signal-drain --apply --limit=100 --passes=5 --until-budget=120s --format=json
35463552
*
35473553
* # Adopt/reconcile unmanaged worktree metadata before cleanup
35483554
* wp datamachine-code workspace worktree reconcile-metadata --dry-run --limit=25 --offset=0 --until-budget=30s --format=json
@@ -3590,7 +3596,7 @@ public function worktree( array $args, array $assoc_args ): void {
35903596
$operation = $args[0] ?? '';
35913597

35923598
if ( '' === $operation ) {
3593-
WP_CLI::error('Usage: wp datamachine-code workspace worktree <add|list|remove|prune|locks|cleanup|cleanup-artifacts|abandoned|bounded-cleanup-eligible-apply|cleanup-eligible-drain|emergency-cleanup|reconcile-metadata|backfill-origin-session|active-no-signal-report|active-no-signal-finalized-apply|active-no-signal-equivalent-clean-apply|active-no-signal-merged-apply|active-no-signal-remote-clean-apply|refresh-context|finalize|mark-cleanup-eligible> [<repo>] [<branch>] [--flags]');
3599+
WP_CLI::error('Usage: wp datamachine-code workspace worktree <add|list|remove|prune|locks|cleanup|cleanup-artifacts|abandoned|bounded-cleanup-eligible-apply|cleanup-eligible-drain|emergency-cleanup|reconcile-metadata|backfill-origin-session|active-no-signal-report|active-no-signal-finalized-apply|active-no-signal-equivalent-clean-apply|active-no-signal-merged-apply|active-no-signal-remote-clean-apply|active-no-signal-drain|refresh-context|finalize|mark-cleanup-eligible> [<repo>] [<branch>] [--flags]');
35943600
return;
35953601
}
35963602

@@ -3604,6 +3610,16 @@ public function worktree( array $args, array $assoc_args ): void {
36043610
return;
36053611
}
36063612

3613+
if ( 'active-no-signal-drain' === $operation ) {
3614+
$result = $this->run_worktree_active_no_signal_drain($assoc_args);
3615+
if ( is_wp_error($result) ) {
3616+
$this->render_workspace_error($result);
3617+
return;
3618+
}
3619+
$this->render_worktree_abandoned_result($result, $assoc_args);
3620+
return;
3621+
}
3622+
36073623
if ( 'locks' === $operation ) {
36083624
$workspace = new Workspace();
36093625
$workspace_path = $workspace->get_path();
@@ -3992,6 +4008,41 @@ private function run_worktree_abandoned_orchestration( array $assoc_args ): arra
39924008
return $ability->execute($input);
39934009
}
39944010

4011+
/**
4012+
* Run the active/no-signal drain ability from CLI flags.
4013+
*
4014+
* @param array<string,mixed> $assoc_args CLI args.
4015+
* @return array<string,mixed>|\WP_Error
4016+
*/
4017+
private function run_worktree_active_no_signal_drain( array $assoc_args ): array|\WP_Error {
4018+
if ( ! empty($assoc_args['force']) ) {
4019+
return new \WP_Error('active_no_signal_drain_refuses_force', 'Active/no-signal drain will not force cleanup. Protected blockers remain blocked.', array( 'status' => 400 ));
4020+
}
4021+
if ( ! empty($assoc_args['discard-unpushed']) ) {
4022+
return new \WP_Error('active_no_signal_drain_refuses_unpushed_discard', 'Active/no-signal drain will not discard unpushed commits.', array( 'status' => 400 ));
4023+
}
4024+
4025+
$ability = wp_get_ability('datamachine-code/workspace-worktree-active-no-signal-drain');
4026+
if ( ! $ability ) {
4027+
return new \WP_Error('active_no_signal_drain_ability_missing', 'Worktree active/no-signal drain ability not available: datamachine-code/workspace-worktree-active-no-signal-drain', array( 'status' => 500 ));
4028+
}
4029+
4030+
$input = array(
4031+
'apply' => ! empty($assoc_args['apply']),
4032+
'source' => self::CLEANUP_CLI_SOURCE,
4033+
);
4034+
foreach ( array( 'limit', 'passes', 'offset', 'stage' ) as $key ) {
4035+
if ( array_key_exists($key, $assoc_args) ) {
4036+
$input[ $key ] = $assoc_args[ $key ];
4037+
}
4038+
}
4039+
if ( isset($assoc_args['until-budget']) ) {
4040+
$input['until_budget'] = $assoc_args['until-budget'];
4041+
}
4042+
4043+
return $ability->execute($input);
4044+
}
4045+
39954046
/**
39964047
* Render abandoned worktree cleanup result.
39974048
*

0 commit comments

Comments
 (0)