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
13 changes: 9 additions & 4 deletions inc/Workspace/Workspace.php
Original file line number Diff line number Diff line change
Expand Up @@ -1807,7 +1807,7 @@ public function worktree_active_no_signal_finalized_apply( array $opts = array()
'written' => $written,
'skipped' => $skipped,
'summary' => $summary,
'pagination' => $this->build_active_no_signal_apply_pagination( (array) ( $report['pagination'] ?? array() ), 'active-no-signal-finalized-apply', $dry_run, $opts),
'pagination' => $this->build_active_no_signal_apply_pagination( (array) ( $report['pagination'] ?? array() ), 'active-no-signal-finalized-apply', $dry_run, $opts, count( $written ) ),
'evidence' => array(
'scope' => 'promote finalized active_no_signal PR evidence into cleanup_eligible metadata',
'safety' => 'Revalidates dirty, unpushed, identity, and closed+merged PR evidence before writing metadata. Does not delete worktrees.',
Expand Down Expand Up @@ -1900,7 +1900,7 @@ public function worktree_active_no_signal_equivalent_clean_apply( array $opts =
'written' => $written,
'skipped' => $skipped,
'summary' => $summary,
'pagination' => $this->build_active_no_signal_apply_pagination( (array) ( $report['pagination'] ?? array() ), 'active-no-signal-equivalent-clean-apply', $dry_run, $opts),
'pagination' => $this->build_active_no_signal_apply_pagination( (array) ( $report['pagination'] ?? array() ), 'active-no-signal-equivalent-clean-apply', $dry_run, $opts, count( $written ) ),
'evidence' => array(
'scope' => 'promote effectively clean upstream-equivalent active_no_signal rows into cleanup_eligible metadata',
'safety' => 'Revalidates upstream-equivalence evidence before writing metadata. Does not delete worktrees.',
Expand Down Expand Up @@ -1993,7 +1993,7 @@ public function worktree_active_no_signal_merged_apply( array $opts = array() ):
'written' => $written,
'skipped' => $skipped,
'summary' => $summary,
'pagination' => $this->build_active_no_signal_apply_pagination( (array) ( $report['pagination'] ?? array() ), 'active-no-signal-merged-apply', $dry_run, $opts),
'pagination' => $this->build_active_no_signal_apply_pagination( (array) ( $report['pagination'] ?? array() ), 'active-no-signal-merged-apply', $dry_run, $opts, count( $written ) ),
'evidence' => array(
'scope' => 'promote clean active_no_signal rows contained in remote default into cleanup_eligible metadata',
'safety' => 'Revalidates clean worktree, no unpushed commits, containment, primary protection, branch identity, and merged-to-default evidence before writing metadata. Does not delete worktrees.',
Expand All @@ -2010,7 +2010,7 @@ public function worktree_active_no_signal_merged_apply( array $opts = array() ):
* @param array<string,mixed> $opts Original operation options.
* @return array<string,mixed>
*/
private function build_active_no_signal_apply_pagination( array $pagination, string $operation, bool $dry_run, array $opts ): array {
private function build_active_no_signal_apply_pagination( array $pagination, string $operation, bool $dry_run, array $opts, int $written_count = 0 ): array {
if ( null === ( $pagination['next_offset'] ?? null ) ) {
$pagination['next_command'] = null;
return $pagination;
Expand All @@ -2027,6 +2027,11 @@ private function build_active_no_signal_apply_pagination( array $pagination, str
$dry_run_arg = $dry_run ? ' --dry-run' : '';
$limit = (int) ( $pagination['limit'] ?? 25 );
$next_offset = (int) $pagination['next_offset'];
if ( ! $dry_run && $written_count > 0 ) {
$current = (int) ( $pagination['offset'] ?? 0 );
$next_offset = max( $current, $next_offset - $written_count );
$pagination['next_offset'] = $next_offset;
}

$pagination['next_command'] = sprintf(
'studio wp datamachine-code workspace worktree %s%s --limit=%d --offset=%d%s --format=json',
Expand Down
10 changes: 9 additions & 1 deletion tests/smoke-worktree-metadata-reconcile.php
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,12 @@ public function worktree_list( ?string $repo = null, ?string $state = null, arra
$merged_report = $ws->worktree_active_no_signal_report(array( 'limit' => 100, 'offset' => 0 ));
$assert(true, ! is_wp_error($merged_report) && ( $merged_report['success'] ?? false ), 'merged-to-default active/no-signal report succeeds');
$merged_rows = array();
$first_merged_offset = null;
foreach ((array) ( $merged_report['rows'] ?? array() ) as $row ) {
$merged_rows[ $row['handle'] ?? '' ] = $row;
if ( null === $first_merged_offset && 'merged_to_default' === (string) ( $row['suggested_action'] ?? '' ) ) {
$first_merged_offset = count($merged_rows) - 1;
}
}
$assert('merged_to_default', $merged_rows['demo@default-merged']['suggested_action'] ?? '', 'clean contained branch is classified merged_to_default');
$assert('fix/foo', $merged_rows['demo@fix-foo']['branch'] ?? '', 'active/no-signal report uses actual checked-out branch with slashes');
Expand All @@ -929,10 +933,14 @@ public function worktree_list( ?string $repo = null, ?string $state = null, arra
$assert('', \DataMachineCode\Workspace\WorktreeContextInjector::get_metadata('demo@default-merged')['cleanup_eligible_at'] ?? '', 'merged-to-default dry-run leaves metadata unchanged');
$merged_page_dry_run = $ws->worktree_active_no_signal_merged_apply(array( 'dry_run' => true, 'limit' => 1, 'offset' => 0, 'internal_budget_label' => '1s', 'internal_budget_seconds' => 60, 'internal_budget_started' => microtime(true) ));
$assert(true, str_contains($merged_page_dry_run['pagination']['next_command'] ?? '', 'active-no-signal-merged-apply --dry-run --limit=1 --offset=1 --until-budget=1s --format=json'), 'merged-to-default dry-run continuation stays on merged apply command');
$assert(true, null !== $first_merged_offset, 'merged-to-default report exposes at least one merged candidate offset');
$merged_page_apply = $ws->worktree_active_no_signal_merged_apply(array( 'limit' => 1, 'offset' => (int) $first_merged_offset, 'internal_budget_label' => '1s', 'internal_budget_seconds' => 60, 'internal_budget_started' => microtime(true) ));
$assert(1, (int) ( $merged_page_apply['summary']['written'] ?? 0 ), 'merged-to-default page apply writes one row');
$assert(true, str_contains($merged_page_apply['pagination']['next_command'] ?? '', 'active-no-signal-merged-apply --limit=1 --offset=' . (int) $first_merged_offset . ' --until-budget=1s --format=json'), 'merged-to-default apply continuation accounts for rows removed from active/no-signal page');

$merged_apply = $ws->worktree_active_no_signal_merged_apply(array( 'limit' => 100, 'offset' => 0 ));
$assert(true, ! is_wp_error($merged_apply) && ( $merged_apply['success'] ?? false ), 'merged-to-default active/no-signal apply succeeds');
$assert(2, (int) ( $merged_apply['summary']['written'] ?? 0 ), 'merged-to-default apply writes only safe merged row metadata');
$assert(1, (int) ( $merged_apply['summary']['written'] ?? 0 ), 'merged-to-default apply writes remaining safe merged row metadata');
$stored_default_merged = \DataMachineCode\Workspace\WorktreeContextInjector::get_metadata('demo@default-merged');
$assert('cleanup_eligible', $stored_default_merged['lifecycle_state'] ?? '', 'merged-to-default apply stores cleanup_eligible state');
$assert('merged', $stored_default_merged['finalized_state'] ?? '', 'merged-to-default apply records merged finalizer state');
Expand Down
Loading