Repository navigation
Give the PR refresh run-loop test something real to observe - #8724
Conversation
📝 WalkthroughWalkthroughA public repository discovery protocol is introduced, adopted by GitMetadataService, and used by pull-request probing and polling. TabManager can inject an alternate discovery implementation, and the related run-loop test now uses a blocking discovery seam. ChangesRepository discovery injection
Estimated code review effort: 2 (Simple) | ~15 minutes Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (23 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
af0c7af to
d704004
Compare
d704004 to
2714541
Compare
1133e54 to
e1487ce
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Greptile SummaryThis PR repairs a test that had silently stopped observing anything real since PR #2797 replaced the
Confidence Score: 5/5Safe to merge — production behavior is unchanged; the only new runtime path is the nil-default fallback that was already the live code path. The protocol extraction is a pure abstraction over two methods PullRequestProbeService was already calling on GitMetadataService. Every production call site receives either gitMetadataService directly or the same value through the nil-coalescing fallback, so the shipped binary is functionally identical to main. The test improvement is honest about what it can observe and removes a non-falsifiable assertion rather than keeping it as false assurance. No actor-isolation mistakes, no new ambient global state, no test seams by the rule definitions, and no blocking primitives in production code were introduced. Files Needing Attention: No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant T as TabManager
participant PPS as PullRequestPollService
participant PRS as PullRequestProbeService
participant GRD as GitRepositoryDiscovering
participant GMS as GitMetadataService
participant STUB as BlockingRepositoryDiscovery
T->>PPS: init with discovery or gitMetadataService
note over T,PPS: production uses GitMetadataService, test injects stub
PPS->>PRS: resolveCandidateSeeds
PRS->>GRD: repositorySlugs forDirectory
GRD-->>GMS: production reads git config in-process
GRD-->>STUB: test increments counter and sleeps 30ms
PRS->>GRD: checkedOutBranch forDirectory
GRD-->>GMS: production reads HEAD file
GRD-->>STUB: test returns notARepository
Reviews (2): Last reviewed commit: "sidebar-git: give the PR refresh run-loo..." | Re-trigger Greptile |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cmuxTests/WorkspacePullRequestSidebarTests.swift (1)
521-619: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy liftMake the regression signal deterministic and sensitive to the actual stall.
The batch limit means this pass runs at most three 30ms discovery calls, so main-thread discovery would block for ~90ms and still pass the 2-second ceiling. The
Thread.sleep/Timer/Datemeasurement also violates the test timing policy.Use a controllable blocking gate: signal that discovery started, enqueue a main-runloop completion signal, and release discovery only after that signal arrives; use a deadline only as cleanup/failure protection. This fails if discovery occupies the main thread without asserting elapsed wall-clock latency.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmuxTests/WorkspacePullRequestSidebarTests.swift` around lines 521 - 619, Replace the Thread.sleep/Timer/Date timing measurement in the regression test with a controllable blocking gate in BlockingRepositoryDiscovery: signal when discovery starts, then block until the test releases it. After triggering updateSurfaceShellActivity, enqueue a main-runloop completion signal and release discovery only when that signal is observed; use a deadline solely to clean up and fail safely. Keep the invocationCounter assertion, and make the test fail when main-thread discovery prevents the queued completion from running rather than relying on elapsed wall-clock latency.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@cmuxTests/WorkspacePullRequestSidebarTests.swift`:
- Around line 521-619: Replace the Thread.sleep/Timer/Date timing measurement in
the regression test with a controllable blocking gate in
BlockingRepositoryDiscovery: signal when discovery starts, then block until the
test releases it. After triggering updateSurfaceShellActivity, enqueue a
main-runloop completion signal and release discovery only when that signal is
observed; use a deadline solely to clean up and fail safely. Keep the
invocationCounter assertion, and make the test fail when main-thread discovery
prevents the queued completion from running rather than relying on elapsed
wall-clock latency.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c8ac529a-d0d3-4289-b67f-ec83abef975f
📒 Files selected for processing (5)
Packages/macOS/CmuxGit/Sources/CmuxGit/GitRepositoryDiscovering.swiftPackages/macOS/CmuxGit/Sources/CmuxGit/PullRequestProbeService.swiftPackages/macOS/CmuxSidebarGit/Sources/CmuxSidebarGit/Service/PullRequestPollService.swiftSources/TabManager.swiftcmuxTests/WorkspacePullRequestSidebarTests.swift
testPullRequestRefreshRepositoryDiscoveryDoesNotBlockMainRunLoop counted calls to a stubbed `git remote -v` subprocess as its proxy for "repository discovery ran". The refresh stopped spawning that process in manaflow-ai#2797, which replaced it with in-process config parsing, so the counter sat at zero and the assertion failed. The checks after it were worse than failing: with no discovery observed, they held whether or not anything happened at all. Repository discovery is the blocking filesystem work the refresh does before it reaches the network, so that is what the test should watch. This adds GitRepositoryDiscovering for the two calls PullRequestProbeService makes while resolving candidate seeds, and lets a host inject it. GitMetadataService conforms and stays the only implementation the app installs, so behavior is unchanged; PullRequestPollService and the probe service accept the protocol instead of the concrete type, which every existing call site already satisfies. The test injects a discovery that counts and sleeps. It resolves no slugs, which keeps the refresh off the GitHub transport and away from `gh auth token`. The test now makes two claims rather than three. The invocation count is the one that can fail for a product reason, and it is the one that was broken. The run-loop tick gap stays as a coarse guard against a seconds-long stall. The old "discovery did not run on the main thread" check is gone, along with the observation box that fed it. `repositorySlugs` is a nonisolated async requirement, so SE-0338 runs it off the caller's actor however the refresh schedules it: the check passed no matter what the product did, including if discovery were rewritten to be awaited inline. A test that cannot fail is not evidence, and keeping it would have implied coverage the test does not have. The run-loop tick gap stays as a coarse guard, and its comment now says why it is loose: 45 seeds times 30ms of injected blocking is 1.35s against a 2.0s ceiling, so this test's own work cannot trip it. It fires only if the product adds a multi-second main-thread stall on top. The counter is renamed to RepositoryDiscoveryInvocationCounter, since it counts discovery calls rather than command-runner calls, and the new TabManager parameter carries a note that it overrides discovery for the pull-request refresh only.
e1487ce to
7525901
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
…r-refresh-discovery-seam
…efresh-discovery-seam
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
|
Review resolution on the final tree:
Verification on the final implementation: targeted runs https://github.com/manaflow-ai/cmux/actions/runs/30895230698 and https://github.com/manaflow-ai/cmux/actions/runs/30896696485 both passed; test wiring, package-lock policy, Swift parsing, |
Problem
testPullRequestRefreshRepositoryDiscoveryDoesNotBlockMainRunLoopused to observe repository discovery by counting calls to a stubbedgit remote -vsubprocess. That stopped being a real signal when #2797 moved remote discovery to in-process config parsing. The counter stayed at zero, and the remaining assertions could pass without proving that discovery had run.Final approach
GitRepositoryDiscoveringfor the two filesystem reads thatPullRequestProbeServicealready performs: resolving GitHub slugs and the checked-out branch.GitMetadataServiceas the only production implementation.PullRequestProbeServiceandPullRequestPollServicefrom the concrete metadata service to that protocol.PullRequestPollServicedirectly with a realTabManagerhost and a test discovery implementation. There is noTabManagertest override and no test-only seam in app source.The protocol and its requirements have API documentation. No user-facing strings, localization data, package lockfiles, Xcode project wiring, or Swift warning budgets change.
Runtime impact
Production behavior is unchanged. The concrete dependency types become protocol existentials, while the app continues to pass the same
GitMetadataServiceinstance through the same polling and probing pipeline.Verification
lint-pbxproj-test-wiring: passed (651 test files checked)check-package-resolved-policy.py: passedgit diff --check: passedBoth targeted Actions runs execute
WorkspacePullRequestSidebarTestsand pass.