@@ -1655,6 +1655,30 @@ public function worktree_active_no_signal_report( array $opts = array() ): array
16551655 $ page = array_slice ($ active , $ offset , $ limit );
16561656
16571657 $ github_cache = array ();
1658+ $ probe_cache = array (
1659+ 'default_ref ' => array (),
1660+ 'github_slug ' => array (),
1661+ 'remote_tracking ' => array (),
1662+ 'commits_outside_default ' => array (),
1663+ 'stats ' => array (
1664+ 'default_ref ' => array (
1665+ 'hits ' => 0 ,
1666+ 'misses ' => 0 ,
1667+ ),
1668+ 'github_slug ' => array (
1669+ 'hits ' => 0 ,
1670+ 'misses ' => 0 ,
1671+ ),
1672+ 'remote_tracking ' => array (
1673+ 'hits ' => 0 ,
1674+ 'misses ' => 0 ,
1675+ ),
1676+ 'commits_outside_default ' => array (
1677+ 'hits ' => 0 ,
1678+ 'misses ' => 0 ,
1679+ ),
1680+ ),
1681+ );
16581682 $ rows = array ();
16591683 $ summary = array (
16601684 'total_active_no_signal ' => $ total ,
@@ -1674,7 +1698,7 @@ public function worktree_active_no_signal_report( array $opts = array() ): array
16741698 break ;
16751699 }
16761700 $ row_started = microtime (true );
1677- $ evidence = $ this ->build_active_no_signal_evidence_row ($ row , $ github_cache );
1701+ $ evidence = $ this ->build_active_no_signal_evidence_row ($ row , $ github_cache, $ probe_cache );
16781702 $ evidence ['elapsed_ms ' ] = (int ) round (( microtime (true ) - $ row_started ) * 1000 );
16791703 $ rows [] = $ evidence ;
16801704 ++$ summary ['inspected ' ];
@@ -1716,9 +1740,10 @@ public function worktree_active_no_signal_report( array $opts = array() ): array
17161740 'summary ' => array_merge ($ summary , array ( 'slow_rows ' => $ this ->summarize_slow_worktree_rows ($ rows ) )),
17171741 'pagination ' => $ pagination ,
17181742 'evidence ' => array (
1719- 'scope ' => 'review-only active_no_signal worktree lifecycle evidence ' ,
1720- 'safety ' => 'No worktrees or remote branches are deleted. Dirty and unpushed probes are evidence only. ' ,
1721- 'budget ' => null === $ budget_context ? null : $ this ->summarize_worktree_loop_budget_context ($ budget_context , $ budget_stopped ),
1743+ 'scope ' => 'review-only active_no_signal worktree lifecycle evidence ' ,
1744+ 'safety ' => 'No worktrees or remote branches are deleted. Dirty and unpushed probes are evidence only. ' ,
1745+ 'budget ' => null === $ budget_context ? null : $ this ->summarize_worktree_loop_budget_context ($ budget_context , $ budget_stopped ),
1746+ 'probe_cache ' => $ probe_cache ['stats ' ],
17221747 ),
17231748 );
17241749 }
@@ -2672,9 +2697,10 @@ private function build_active_no_signal_finalized_apply_skip( array $row, string
26722697 *
26732698 * @param array<string,mixed> $row Inventory skip row.
26742699 * @param array<string,mixed> $github_cache Run-local GitHub cache.
2700+ * @param array<string,mixed> $probe_cache Run-local git probe cache.
26752701 * @return array<string,mixed>
26762702 */
2677- private function build_active_no_signal_evidence_row ( array $ row , array &$ github_cache ): array {
2703+ private function build_active_no_signal_evidence_row ( array $ row , array &$ github_cache, array & $ probe_cache ): array {
26782704 $ handle = (string ) ( $ row ['handle ' ] ?? '' );
26792705 $ repo = (string ) ( $ row ['repo ' ] ?? '' );
26802706 $ branch = (string ) ( $ row ['branch ' ] ?? '' );
@@ -2684,11 +2710,16 @@ private function build_active_no_signal_evidence_row( array $row, array &$github
26842710 $ metadata = is_array ($ row ['metadata ' ] ?? null ) ? $ row ['metadata ' ] : array ();
26852711 $ branch_probe = null ;
26862712 if ( '' !== $ path && is_dir ($ path ) ) {
2687- $ branch_probe = $ this ->run_git ($ path , 'branch --show-current ' , self ::CLEANUP_GIT_PROBE_TIMEOUT );
2688- if ( ! is_wp_error ($ branch_probe ) && ! $ this ->is_git_timeout_error ($ branch_probe ) ) {
2689- $ actual_branch = trim ( (string ) ( $ branch_probe ['output ' ] ?? '' ) );
2690- if ( '' !== $ actual_branch ) {
2691- $ branch = $ actual_branch ;
2713+ $ head_branch = $ this ->resolve_worktree_branch_from_head_file ($ path );
2714+ if ( null !== $ head_branch && '' !== $ head_branch ) {
2715+ $ branch = $ head_branch ;
2716+ } else {
2717+ $ branch_probe = $ this ->run_git ($ path , 'branch --show-current ' , self ::CLEANUP_GIT_PROBE_TIMEOUT );
2718+ if ( ! is_wp_error ($ branch_probe ) && ! $ this ->is_git_timeout_error ($ branch_probe ) ) {
2719+ $ actual_branch = trim ( (string ) ( $ branch_probe ['output ' ] ?? '' ) );
2720+ if ( '' !== $ actual_branch ) {
2721+ $ branch = $ actual_branch ;
2722+ }
26922723 }
26932724 }
26942725 }
@@ -2749,18 +2780,16 @@ private function build_active_no_signal_evidence_row( array $row, array &$github
27492780 }
27502781
27512782 $ remote_ref = 'refs/remotes/origin/ ' . $ branch ;
2752- $ remote = $ this ->time_worktree_probe ($ out ['probe_timings_ms ' ], 'remote_tracking ' , fn () => $ this ->run_git ($ primary_path , sprintf ( ' rev-parse --verify --quiet %s ' , escapeshellarg ( $ remote_ref)), self :: CLEANUP_GIT_PROBE_TIMEOUT ));
2783+ $ remote = $ this ->time_worktree_probe ($ out ['probe_timings_ms ' ], 'remote_tracking ' , fn () => $ this ->cached_active_no_signal_remote_tracking_probe ($ primary_path , $ remote_ref, $ probe_cache ));
27532784 $ out ['remote_tracking ' ] = ! is_wp_error ($ remote ) && ! $ this ->is_git_timeout_error ($ remote );
27542785
2755- $ default_ref = $ this ->time_worktree_probe ($ out ['probe_timings_ms ' ], 'default_ref ' , fn () => $ this ->resolve_remote_default_ref ($ primary_path , self :: CLEANUP_GIT_PROBE_TIMEOUT ));
2786+ $ default_ref = $ this ->time_worktree_probe ($ out ['probe_timings_ms ' ], 'default_ref ' , fn () => $ this ->cached_active_no_signal_default_ref_probe ($ primary_path , $ probe_cache ));
27562787 if ( is_string ($ default_ref ) && '' !== $ default_ref ) {
27572788 $ out ['default_ref ' ] = $ default_ref ;
27582789 $ outside = $ this ->time_worktree_probe (
2759- $ out ['probe_timings_ms ' ], 'commits_outside_default ' , fn () => $ this ->run_git (
2760- $ primary_path ,
2761- sprintf ('rev-list --count %s..%s ' , escapeshellarg ($ default_ref ), escapeshellarg ('refs/heads/ ' . $ branch )),
2762- self ::CLEANUP_GIT_PROBE_TIMEOUT
2763- )
2790+ $ out ['probe_timings_ms ' ],
2791+ 'commits_outside_default ' ,
2792+ fn () => $ this ->cached_active_no_signal_commits_outside_default_probe ($ primary_path , $ default_ref , $ branch , $ probe_cache )
27642793 );
27652794 if ( ! is_wp_error ($ outside ) && ! $ this ->is_git_timeout_error ($ outside ) ) {
27662795 $ out ['commits_outside_default ' ] = (int ) trim ( (string ) ( $ outside ['output ' ] ?? '' ));
@@ -2776,7 +2805,7 @@ private function build_active_no_signal_evidence_row( array $row, array &$github
27762805 if ( (int ) ( $ out ['dirty ' ] ?? 0 ) > 0 || (int ) ( $ out ['unpushed ' ] ?? 0 ) > 0 ) {
27772806 $ out ['pr_lookup_skipped ' ] = 'dirty_or_unpushed_rows_are_always_manual_review ' ;
27782807 } else {
2779- $ slug = $ this ->time_worktree_probe ($ out ['probe_timings_ms ' ], 'github_slug ' , fn () => $ this ->resolve_github_slug ($ primary_path ));
2808+ $ slug = $ this ->time_worktree_probe ($ out ['probe_timings_ms ' ], 'github_slug ' , fn () => $ this ->cached_active_no_signal_github_slug_probe ($ primary_path, $ probe_cache ));
27802809 if ( null !== $ slug ) {
27812810 $ pr = $ this ->time_worktree_probe ($ out ['probe_timings_ms ' ], 'github_pr_lookup ' , fn () => $ this ->find_pr_for_branch_direct ($ slug , $ branch , $ github_cache , false ));
27822811 if ( is_wp_error ($ pr ) ) {
@@ -2793,6 +2822,106 @@ private function build_active_no_signal_evidence_row( array $row, array &$github
27932822 return $ out ;
27942823 }
27952824
2825+ /**
2826+ * Cache a remote-tracking ref existence probe for an active/no-signal report run.
2827+ *
2828+ * @param string $primary_path Primary checkout path.
2829+ * @param string $remote_ref Fully-qualified remote-tracking ref.
2830+ * @param array<string,mixed> $probe_cache Run-local git probe cache.
2831+ * @return array<string,mixed>|\WP_Error Git result or timeout/error.
2832+ */
2833+ private function cached_active_no_signal_remote_tracking_probe ( string $ primary_path , string $ remote_ref , array &$ probe_cache ): array |\WP_Error {
2834+ $ key = $ primary_path . '# ' . $ remote_ref ;
2835+ if ( array_key_exists ($ key , $ probe_cache ['remote_tracking ' ] ?? array ()) ) {
2836+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'remote_tracking ' , true );
2837+ return $ probe_cache ['remote_tracking ' ][ $ key ];
2838+ }
2839+
2840+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'remote_tracking ' , false );
2841+ $ result = $ this ->run_git ($ primary_path , sprintf ('rev-parse --verify --quiet %s ' , escapeshellarg ($ remote_ref )), self ::CLEANUP_GIT_PROBE_TIMEOUT );
2842+ $ probe_cache ['remote_tracking ' ][ $ key ] = $ result ;
2843+ return $ result ;
2844+ }
2845+
2846+ /**
2847+ * Cache the remote default ref for an active/no-signal report run.
2848+ *
2849+ * @param string $primary_path Primary checkout path.
2850+ * @param array<string,mixed> $probe_cache Run-local git probe cache.
2851+ * @return string|\WP_Error|null Fully-qualified remote default ref, timeout/error, or null.
2852+ */
2853+ private function cached_active_no_signal_default_ref_probe ( string $ primary_path , array &$ probe_cache ): string |\WP_Error |null {
2854+ if ( array_key_exists ($ primary_path , $ probe_cache ['default_ref ' ] ?? array ()) ) {
2855+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'default_ref ' , true );
2856+ return $ probe_cache ['default_ref ' ][ $ primary_path ];
2857+ }
2858+
2859+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'default_ref ' , false );
2860+ $ result = $ this ->resolve_remote_default_ref ($ primary_path , self ::CLEANUP_GIT_PROBE_TIMEOUT );
2861+ $ probe_cache ['default_ref ' ][ $ primary_path ] = $ result ;
2862+ return $ result ;
2863+ }
2864+
2865+ /**
2866+ * Cache commits-outside-default probes for an active/no-signal report run.
2867+ *
2868+ * @param string $primary_path Primary checkout path.
2869+ * @param string $default_ref Fully-qualified remote default ref.
2870+ * @param string $branch Local branch name.
2871+ * @param array<string,mixed> $probe_cache Run-local git probe cache.
2872+ * @return array<string,mixed>|\WP_Error Git result or timeout/error.
2873+ */
2874+ private function cached_active_no_signal_commits_outside_default_probe ( string $ primary_path , string $ default_ref , string $ branch , array &$ probe_cache ): array |\WP_Error {
2875+ $ key = $ primary_path . '# ' . $ default_ref . '# ' . $ branch ;
2876+ if ( array_key_exists ($ key , $ probe_cache ['commits_outside_default ' ] ?? array ()) ) {
2877+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'commits_outside_default ' , true );
2878+ return $ probe_cache ['commits_outside_default ' ][ $ key ];
2879+ }
2880+
2881+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'commits_outside_default ' , false );
2882+ $ result = $ this ->run_git (
2883+ $ primary_path ,
2884+ sprintf ('rev-list --count %s..%s ' , escapeshellarg ($ default_ref ), escapeshellarg ('refs/heads/ ' . $ branch )),
2885+ self ::CLEANUP_GIT_PROBE_TIMEOUT
2886+ );
2887+ $ probe_cache ['commits_outside_default ' ][ $ key ] = $ result ;
2888+ return $ result ;
2889+ }
2890+
2891+ /**
2892+ * Cache the GitHub slug for an active/no-signal report run.
2893+ *
2894+ * @param string $primary_path Primary checkout path.
2895+ * @param array<string,mixed> $probe_cache Run-local git probe cache.
2896+ * @return string|null owner/repo or null when origin is not GitHub.
2897+ */
2898+ private function cached_active_no_signal_github_slug_probe ( string $ primary_path , array &$ probe_cache ): ?string {
2899+ if ( array_key_exists ($ primary_path , $ probe_cache ['github_slug ' ] ?? array ()) ) {
2900+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'github_slug ' , true );
2901+ return $ probe_cache ['github_slug ' ][ $ primary_path ];
2902+ }
2903+
2904+ $ this ->record_active_no_signal_probe_cache_stat ($ probe_cache , 'github_slug ' , false );
2905+ $ result = $ this ->resolve_github_slug ($ primary_path );
2906+ $ probe_cache ['github_slug ' ][ $ primary_path ] = $ result ;
2907+ return $ result ;
2908+ }
2909+
2910+ /**
2911+ * Record active/no-signal probe cache hit/miss counts.
2912+ *
2913+ * @param array<string,mixed> $probe_cache Run-local git probe cache.
2914+ * @param string $bucket Probe cache bucket.
2915+ * @param bool $hit Whether the lookup was a cache hit.
2916+ */
2917+ private function record_active_no_signal_probe_cache_stat ( array &$ probe_cache , string $ bucket , bool $ hit ): void {
2918+ $ field = $ hit ? 'hits ' : 'misses ' ;
2919+ if ( ! isset ($ probe_cache ['stats ' ][ $ bucket ][ $ field ]) ) {
2920+ $ probe_cache ['stats ' ][ $ bucket ][ $ field ] = 0 ;
2921+ }
2922+ ++$ probe_cache ['stats ' ][ $ bucket ][ $ field ];
2923+ }
2924+
27962925 /**
27972926 * Build patch-equivalence evidence for clean active/no-signal worktrees.
27982927 *
0 commit comments