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
28 changes: 27 additions & 1 deletion inc/Workspace/WorkspaceRepositoryLifecycle.php
Original file line number Diff line number Diff line change
Expand Up @@ -1000,10 +1000,13 @@ public function remove_repo( string $handle ): array|\WP_Error {
* @return array{success: bool, name?: string, repo?: string, is_worktree?: bool, is_context?: bool, path?: string|null, branch?: string|null, remote?: string|null, commit?: string|null, dirty?: int, workspace_capacity?: array, primary_freshness?: array|null, workspace_policy?: array}|\WP_Error
*/
public function show_repo( string $handle, bool $refresh = false ): array|\WP_Error {
$show_started = microtime(true);
$registry_lookup_started = microtime(true);
$requested_handle = $handle;
$context_policy = null;
$parsed = $this->parse_handle($handle);
$repo_path = $this->workspace_path . '/' . $parsed['dir_name'];
$registry_lookup_ms = (int) round(( microtime(true) - $registry_lookup_started ) * 1000);
$inspection = WorkspaceTargetInspector::inspect($repo_path, $parsed['dir_name']);
if ( is_wp_error($inspection) ) {
return $inspection;
Expand Down Expand Up @@ -1066,10 +1069,15 @@ public function show_repo( string $handle, bool $refresh = false ): array|\WP_Er
: null
)
: null;
$remote_freshness_started = microtime(true);
if ( $refresh && is_array($primary_freshness) ) {
$primary_freshness = $this->refresh_primary_freshness_report($repo_path, $parsed['dir_name'], $primary_freshness);
}
$remote_freshness_ms = (int) round(( microtime(true) - $remote_freshness_started ) * 1000);

$capacity_started = microtime(true);
$capacity = WorktreeDiskBudget::inspect($this->workspace_path);
$capacity_ms = (int) round(( microtime(true) - $capacity_started ) * 1000);
$result = array(
'success' => true,
'name' => null !== $context_policy ? (string) $context_policy['alias'] : $parsed['dir_name'],
Expand All @@ -1081,15 +1089,33 @@ public function show_repo( string $handle, bool $refresh = false ): array|\WP_Er
'remote' => $inspection['remote'] ?? null,
'commit' => $inspection['commit'] ?? null,
'dirty' => (int) ( $inspection['dirty'] ?? 0 ),
'workspace_capacity' => WorktreeDiskBudget::inspect($this->workspace_path),
'workspace_capacity' => $capacity,
'primary_freshness' => $primary_freshness,
);
$optional_enrichments_started = microtime(true);
if ( $parsed['is_worktree'] ) {
$result['readiness'] = WorktreeContextInjector::bootstrap_readiness(WorktreeContextInjector::get_metadata($parsed['dir_name']));
}
if ( null !== $context_policy ) {
$result['workspace_policy'] = WorkspaceAliasResolver::policy_attestation($handle);
}
if ( function_exists('do_action') ) {
$probe_timings = is_array($inspection['probe_timings_ms'] ?? null) ? $inspection['probe_timings_ms'] : array();
do_action(
'datamachine_code_workspace_show_profiled',
array(
'handle' => $requested_handle,
'timings_ms' => array(
'registry_lookup' => $registry_lookup_ms,
'capacity' => $capacity_ms,
'git_status' => array_sum(array_map('intval', array_intersect_key($probe_timings, array_flip(array( 'branch', 'remote', 'commit', 'status' ))))),
'remote_freshness' => $remote_freshness_ms,
'optional_enrichments' => (int) round(( microtime(true) - $optional_enrichments_started ) * 1000),
'total' => (int) round(( microtime(true) - $show_started ) * 1000),
),
)
);
}

return $result;
}
Expand Down
12 changes: 6 additions & 6 deletions inc/Workspace/WorktreeDiskBudget.php
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,11 @@ private static function format_bytes( int|float $bytes ): string {
}

/**
* Count worktree-like directories cheaply without consulting every primary.
* Count managed worktree names without probing every workspace entry.
*
* Workspace lifecycle owns the `<repo>@<slug>` directory convention. Counting
* its names from the root snapshot avoids one remote-filesystem stat per
* unrelated worktree while preserving the existing aggregate evidence.
*
* @param string $workspace_path Workspace root path.
* @return int
Expand All @@ -1018,11 +1022,7 @@ private static function count_worktree_like_dirs( string $workspace_path ): int

$count = 0;
foreach ( $entries as $entry ) {
if ( '.' === $entry || '..' === $entry || ! str_contains($entry, '@') ) {
continue;
}

if ( is_dir($workspace_path . '/' . $entry) ) {
if ( '.' !== $entry && '..' !== $entry && str_contains($entry, '@') ) {
++$count;
}
}
Expand Down
22 changes: 14 additions & 8 deletions inc/Workspace/workspace-target-probe.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
$git_command = (string) ( $argv[3] ?? 'git' );
$probe_group_pid = 0;
$probe_process = null;
$probe_timings_ms = array();

// ProcessRunner normally creates this group. Establish it here as well so its
// direct-process fallback can interrupt this worker without orphaning a probe.
Expand Down Expand Up @@ -86,22 +87,26 @@ static function () use ( &$probe_process, $probe_group_pid ): void {
};

fwrite(STDERR, "DMC_BOUNDARY:filesystem:is_dir\n");
$filesystem_started = microtime(true);
if ( '' !== $filesystem_probe ) {
$result = $run_probe($filesystem_probe . ' ' . escapeshellarg($workspace_path));
$exists = 0 === $result['exit'] && '1' === trim(implode("\n", $result['output']));
} else {
$exists = is_dir($workspace_path);
}
$probe_timings_ms['filesystem'] = (int) round(( microtime(true) - $filesystem_started ) * 1000);
if ( ! $exists ) {
fwrite(STDOUT, (string) json_encode(array( 'exists' => false ), JSON_UNESCAPED_SLASHES));
fwrite(STDOUT, (string) json_encode(array( 'exists' => false, 'probe_timings_ms' => $probe_timings_ms ), JSON_UNESCAPED_SLASHES));
exit(0);
}

/** @return string|null */
$git_probe = static function ( string $operation, string $args ) use ( $workspace_path, $git_command, $run_probe ): ?string {
$git_probe = static function ( string $operation, string $args ) use ( $workspace_path, $git_command, $run_probe, &$probe_timings_ms ): ?string {
fwrite(STDERR, 'DMC_BOUNDARY:git:' . $operation . "\n");
$started = microtime(true);
$command = $git_command . ' --no-optional-locks -C ' . escapeshellarg($workspace_path) . ' ' . $args;
$result = $run_probe($command, true);
$probe_timings_ms[ $operation ] = (int) round(( microtime(true) - $started ) * 1000);
if ( 0 !== $result['exit'] ) {
return null;
}
Expand All @@ -123,13 +128,14 @@ static function () use ( &$probe_process, $probe_group_pid ): void {
STDOUT,
(string) json_encode(
array(
'exists' => true,
'branch' => '' !== (string) $branch ? $branch : null,
'remote' => '' !== (string) $remote ? $remote : null,
'commit' => '' !== (string) $commit ? $commit : null,
'dirty' => $dirty,
'branch_status' => $branch_status,
'exists' => true,
'branch' => '' !== (string) $branch ? $branch : null,
'remote' => '' !== (string) $remote ? $remote : null,
'commit' => '' !== (string) $commit ? $commit : null,
'dirty' => $dirty,
'branch_status' => $branch_status,
'tracking_ref_observed_at' => $tracking_ref_observed_at,
'probe_timings_ms' => $probe_timings_ms,
),
JSON_UNESCAPED_SLASHES
)
Expand Down
40 changes: 38 additions & 2 deletions tests/workspace-command-startup-bounds.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ class BaseCommand {}
}

namespace DataMachineCode\Workspace {
function is_dir( string $path ): bool {
if ( ! empty($GLOBALS['dmc_test_record_workspace_is_dir']) ) {
$GLOBALS['dmc_test_workspace_is_dir_paths'][] = $path;
}
return \is_dir($path);
}

function disk_free_space( string $path ): float|false {
return $GLOBALS['dmc_test_disk_free_bytes'] ?? \disk_free_space($path);
}
Expand Down Expand Up @@ -50,12 +57,15 @@ public static function error( string $message ): void { throw new \RuntimeExcept

/** @var array<string,array<int,array{priority:int,callback:callable}>> */
$GLOBALS['dmc_test_actions'] = array();
$GLOBALS['dmc_test_emitted_actions'] = array();
$GLOBALS['dmc_test_get_option_calls'] = 0;
$GLOBALS['dmc_test_mutation_calls'] = 0;
$GLOBALS['dmc_test_options'] = array();
$GLOBALS['dmc_test_filters'] = array();
$GLOBALS['dmc_test_disk_free_bytes'] = null;
$GLOBALS['dmc_test_disk_total_bytes'] = null;
$GLOBALS['dmc_test_record_workspace_is_dir'] = false;
$GLOBALS['dmc_test_workspace_is_dir_paths'] = array();

function startup_bounds_assert( bool $condition, string $message ): void {
if ( ! $condition ) {
Expand Down Expand Up @@ -91,6 +101,7 @@ function apply_filters( string $hook, mixed $value, mixed ...$args ): mixed {
return $value;
}
function do_action( string $hook, mixed ...$args ): void {
$GLOBALS['dmc_test_emitted_actions'][ $hook ][] = $args;
$callbacks = $GLOBALS['dmc_test_actions'][ $hook ] ?? array();
usort($callbacks, static fn ( array $left, array $right ): int => $left['priority'] <=> $right['priority']);
foreach ( $callbacks as $entry ) {
Expand Down Expand Up @@ -190,20 +201,45 @@ function startup_bounds_remove_tree( string $path ): void {
// Make capacity pressure deterministic while retaining the actual CLI ->
// WorkspaceAbilities -> Workspace show path. The warning/refusal output may
// only consume this already-measured capacity result, not bootstrap hygiene.
for ( $index = 0; $index < 101; ++$index ) {
for ( $index = 0; $index < 176; ++$index ) {
mkdir($workspace . '/unrelated@' . str_pad((string) $index, 4, '0', STR_PAD_LEFT));
}
$GLOBALS['dmc_test_disk_total_bytes'] = (float) ( 100 * 1024 * 1024 * 1024 );
$GLOBALS['dmc_test_disk_free_bytes'] = (float) ( 15 * 1024 * 1024 * 1024 );
$git_probe_log = $workspace . '/target-git-probes';
$git_probe = $workspace . '/target-git-probe.sh';
file_put_contents($git_probe, "#!/bin/sh\nprintf '%s\\n' \"\$*\" >> " . escapeshellarg($git_probe_log) . "\nexec git \"\$@\"\n");
chmod($git_probe, 0755);
$GLOBALS['dmc_test_filters']['datamachine_code_workspace_target_git_command'] = escapeshellarg($git_probe);
$GLOBALS['dmc_test_record_workspace_is_dir'] = true;
$produced_show = \DataMachineCode\Abilities\WorkspaceAbilities::showRepo(array( 'name' => 'target' ));
$GLOBALS['dmc_test_record_workspace_is_dir'] = false;
unset($GLOBALS['dmc_test_filters']['datamachine_code_workspace_target_git_command']);
startup_bounds_assert(! is_wp_error($produced_show), 'WorkspaceAbilities::showRepo did not produce the bounded local result.');
$produced_capacity = $produced_show['workspace_capacity'] ?? null;
startup_bounds_assert(is_array($produced_capacity), 'WorkspaceAbilities::showRepo did not emit workspace_capacity.');
foreach ( array( 'workspace_path', 'filesystem_free_bytes', 'filesystem_total_bytes', 'worktree_count', 'status', 'warnings', 'trigger_reasons', 'typed_trigger_reasons', 'creation_allowed', 'diagnostic_id', 'advisory_fingerprint', 'evidence_reference', 'recovery_actions' ) as $field ) {
startup_bounds_assert(array_key_exists($field, $produced_capacity), sprintf('WorkspaceAbilities::showRepo emitted an incomplete workspace_capacity: missing %s.', $field));
}
startup_bounds_assert($workspace === $produced_capacity['workspace_path'], 'WorkspaceAbilities::showRepo emitted capacity for the wrong workspace.');
startup_bounds_assert(101 === $produced_capacity['worktree_count'], 'WorkspaceAbilities::showRepo did not measure the bounded worktree fixture.');
startup_bounds_assert(176 === $produced_capacity['worktree_count'], 'WorkspaceAbilities::showRepo did not preserve the large-workspace capacity count.');
$unrelated_probes = array_filter(
$GLOBALS['dmc_test_workspace_is_dir_paths'],
static fn ( string $path ): bool => str_starts_with($path, $workspace . '/unrelated@')
);
startup_bounds_assert(array() === array_values($unrelated_probes), 'Targeted show enumerated unrelated worktree paths during capacity inspection.');
$git_probes = file($git_probe_log, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: array();
startup_bounds_assert(4 === count($git_probes), 'Targeted show did not retain its four bounded local Git probes.');
foreach ( $git_probes as $git_probe_args ) {
startup_bounds_assert(str_starts_with($git_probe_args, '--no-optional-locks -C ' . $workspace . '/target '), 'Targeted show ran Git against an unrelated checkout.');
}
$profiles = $GLOBALS['dmc_test_emitted_actions']['datamachine_code_workspace_show_profiled'] ?? array();
$profile = end($profiles)[0] ?? null;
startup_bounds_assert(is_array($profile) && 'target' === ($profile['handle'] ?? null), 'Targeted show did not emit its phase timing profile.');
startup_bounds_assert(
array( 'registry_lookup', 'capacity', 'git_status', 'remote_freshness', 'optional_enrichments', 'total' ) === array_keys((array) ($profile['timings_ms'] ?? array())),
'Targeted show timing profile did not retain every required phase.'
);
startup_bounds_assert('warning' === $produced_capacity['status'], 'WorkspaceAbilities::showRepo did not preserve the fixture capacity status.');
startup_bounds_assert(in_array('worktree_count_warning_threshold', $produced_capacity['trigger_reasons'], true), 'WorkspaceAbilities::showRepo did not emit the worktree capacity trigger.');
$produced_reasons = \DataMachineCode\Workspace\WorktreeDiskBudget::format_trigger_reasons($produced_capacity);
Expand Down
1 change: 1 addition & 0 deletions tests/workspace-show-cli-format-contract.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ function workspace_show_cli_assert( bool $condition, string $message ): void {
);

WP_CLI::$logs = array();
unset(WorkspaceAbilities::$result['primary_freshness']);
WorkspaceAbilities::$result['workspace_capacity'] = array(
'status' => 'warning',
'creation_allowed' => true,
Expand Down
Loading