Repository navigation
Conversation
…application files for GitHub Command Center
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…fecycle AppState gains triage-sorted PR lists, panel content state machine, health status, polling lifecycle management, staleness tracking, and session reset. PRState gains urgencyScore, displayRole, and triage category logic. AppError gains Equatable for test assertions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…andling Full implementation: search-based PR discovery, concurrent per-PR detail fetching (reviews, check runs, commit statuses), mergeable_state retry, duplicate commit status deduplication, ETag/304 response caching, and independent core/search rate limit bucket tracking. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dynamic poll interval scales with PR count (60/120/300s) to stay within rate limits. Auth and no-token errors stop polling immediately. Rate limit errors back off until reset. Network errors use exponential backoff capped at 60s. Username is validated once then reused across poll cycles. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Detects CI passing→failing, changes requested, approved, merge conflicts, review requested, and PR closed transitions between poll cycles. Skips notifications on first poll (no baseline). Adds notificationHandler injection point so tests can capture fired notifications without touching UNUserNotificationCenter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PRListView: triage sections (Needs Your Action / Waiting on Others), recently-closed transient section, shimmer skeleton on first load, stale/rate-limit warning bars, auth error banners, empty/setup/auth-failed states, footer with relative timestamp, refresh shortcut, and launch-at-login toggle. SettingsView: token save/validate flow with live border feedback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds GitHubCommandCenterTests bundle.unit-test target with correct BUNDLE_LOADER/TEST_HOST wiring so tests run in the app's process and can access @testable imports. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PRStateTests: triage, urgency, sort order, display role, merge status. NotificationServiceTests: all transition cases, no-repeat guards, disappeared PRs, first-poll silence. PollingEngineTests: intervals, auth/network/rate-limit error handling, staleness, session reset, panel state. GitHubRESTClientTests: HTTP status codes, mergeable_state mapping, CI/review/commit status parsing, pagination, ETag/304 caching. KeychainServiceTests: CRUD, overwrite, unicode. Helpers: MockGitHubDataSource, MockURLProtocol, PRState.fixture(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes the build-only fallback that swallowed test failures with "No tests yet — build-only CI passing". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add early-exit guard when app bundle path is empty in release workflow. Consolidate xcuserdata/xcuserstate patterns and remove stale SPM entries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add project summary and CLI build/test commands to CLAUDE.md. Add TESTING_GUIDE.md with step-by-step instructions for building, running, and manually verifying the app. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
changesRequested now only contributes to urgency score on PRs you authored — requesting reviewers shouldn't boost urgency for others. Extract PRState.compareForNeedsAction as a static method for reuse. Add AppError.incompleteSearchResults for GitHub search API edge case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Open up init with configurable service/account names so tests can use isolated keychain items without touching the real credential. Add distinct decodingFailed error case; make KeychainError Equatable; throw instead of silently returning nil on data corruption. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a 304 arrives with no cached response, retry once without the ETag rather than throwing a network error. Throw incompleteSearchResults when GitHub signals the search index was not fully consulted. Track the search rate-limit reset date independently from the core bucket so backoff is accurate for both buckets. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Only fire "PR closed" notifications for PRs you authored — no need to notify about others' PRs being closed. Add PollingEngine.reset() to clear previousPRs and consecutiveFailures without stopping the task. Make recentlyClosed clear task cancellable and inject the delay for test control. Propagate Keychain errors as authError rather than swallowing them. Handle incompleteSearchResults without resetting the consecutive-failure counter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deduplicate the state-clearing logic into a private helper used by both clearSessionStateForNewSession and resetPolling. Also call pollingEngine?.reset() before stopping so previousPRs and failure counters are cleared on token changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace custom hover tooltip in StatusDotView with native .help() modifier — simpler and works correctly in menu bar panels. Add unvalidated token state in SettingsView so editing a saved token clears the valid indicator immediately; propagate Keychain load errors to the UI; add @mainactor to saveToken Task. Sync launchAtLogin toggle state on panel appear rather than only at init. Add #Preview macros to MenuBarIconView. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace XCTest with Swift Testing (@Suite/@Test/#expect/Issue.record) across all test files. Each test now creates its own Harness to avoid shared state between cases. New cases: 304-without-cached-response retries without ETag; incompleteSearchResults is surfaced through validateTokenForAppAccess; incomplete_results field added to search stub payloads. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces mise.toml with build/test/check/fix tasks, .swift-format and .swiftlint.yml for enforced code style, and hk.pkl for pre-commit hooks. Adds .mise.local.toml to .gitignore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces inline xcodebuild invocations with mise-action setup and `mise run check` / `mise run build-release`, reducing duplication. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces raw xcodebuild commands with mise task equivalents in CLAUDE.md. Rewrites TESTING_GUIDE.md as a development setup guide covering mise tasks, swift-format/swiftlint configuration, hk pre-commit hooks, and manual testing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Initial formatting pass using the newly added style tools. Changes include trailing commas in collections, switch-case spacing, multi-line wrapping, import ordering, and short identifier renames (q→query, s→status, u→authenticatedUsername). Also removes force-unwraps in SettingsView and PRStateFixtures per swiftlint rules. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add PollingControlling protocol so tests can stub the engine without touching Keychain. AppState gains makePollingEngine and requestNotificationPermission injection points. PollingEngine adds PollOutcome enum and handleAuthFailure helper to reduce duplication. GitHubCommandCenterApp gets @mainactor and an injectable init so AppStateTests can wire up the full startup path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a network or non-auth error occurs before any PRs are loaded, show a dedicated error screen with a wifi icon, error description, and a Retry button that calls forceRefresh(). Refactors the duplicate authErrorBanner/noTokenBanner into a shared actionBanner helper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce a private RequestError type to layer HTTP errors before mapping to AppError, letting fetchCheckRuns swallow 403/404 and fall back to commit statuses instead of crashing the poll cycle. Fix CI priority so a failing check beats a co-existing pending one. Fix review status so DISMISSED is kept (clearing prior approvals), not silently dropped. Remove unused rate-limit tracking state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
wasFailing replaces the old wasPassingOrNone check so a pending→failing transition also triggers the notification. Extract notifyChangesRequestedIfNeeded and notifyApprovalIfNeeded helpers to reduce duplication. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move Harness and stubFullPRFlow into a shared GitHubRESTClientTestSupport file. Add AppStateTests covering injection startup and loadError state. New REST client tests: 403/404 check-run fallback, failed-beats-pending CI priority, DISMISSED review clearing approval. New notification test: pending→failing fires. New polling test: networkError returns continueImmediately. Replace fixed Task.sleep in reset test with a waitUntil helper. Harden Keychain setup helpers to throw on failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Checks: Read to the required fine-grained PAT scopes in both SettingsView and the testing guide. Fix absolute file paths to relative links in the testing guide. Add a V1.1 TODO for team-based review request support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ild errors AppError: extract RateLimitContext struct so the reset date and human- readable description are computed once at throw time, removing the shared NSLock on the formatter. GitHubRESTClient: replace the array-based LRU touch (O(n)) with a doubly-linked list (O(1)). Add a dedicated checkRunsMaxPageLimit constant and log the status code when fetchCheckRuns falls back early. Remove the silent-drop pattern in concurrent PR state builds — errors now propagate so callers can surface them. Add a test confirming that a 500 on one PR throws rather than silently dropping the PR. PollingEngine: update RateLimitContext call sites; move clearRecentlyClosedTaskID assignment before the Task is created to close a narrow race where the task could complete before the ID was stored. Tests: update pagination test to verify the final non-full page stops further requests; fix MockURLProtocol to skip the Content-Type header on 304 responses. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move "Your Draft PRs" section after "Waiting on Others" in the triage categories doc to match the visual order in the panel. Rephrase two bug descriptions in improvements-to-make.md for clarity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…o PR trigger - Pin CI and release workflows to Xcode 26.4 (was 16.4) - Add `mise run fix` step before `mise run check` and verify neither produces dirty changes - Switch CI trigger from branch pushes to pull_request only, with concurrency cancellation - Add `permissions: contents: read` and skip draft PRs - Update project.yml and TESTING_GUIDE.md to match new Xcode version Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Normalise the version string to standard three-part semver. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes the top-level Spacing enum and Font extension, nesting them as Theme.Spacing and Theme.Fonts respectively. All view call sites updated. Co-located fixes included where changes touched the same lines: - MenuBarIconView: set tick=true on task start so animation begins immediately - StatusDotView: replace fixed padding with minWidth/minHeight frame for accessibility tap target - SettingsView: save keychain token before computing success outcome to avoid a cancelled-task gap Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GitHub's search API returns at most 1,000 items. Previously the client would silently page through all results; now it stops at 1,000 and attaches a warning message to the fetch result. - Add PRFetchResult (wraps [PRState] + optional warningMessage) - Update GitHubDataSource protocol to return PRFetchResult - GitHubRESTClient: stop paging once 1,000 items collected, set searchCapWarningMessage when totalCount > 1,000 - Fix mergeability retry condition: use && instead of || so the retry only fires when the field is both present and unknown - AppState.PanelState gains a warningMessage field propagated from each poll cycle - PollingEngine: consume PRFetchResult, propagate warningMessage, refactor catch branches into private helper methods, add OSLog - PRListView: show a yellow warning bar when warningMessage is set Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- MockGitHubDataSource updated to return PRFetchResult; marked @unchecked Sendable - PollingEngineTests: add successResult() helper, update all call sites, add tests for serverError retry, paginationLimitExceeded regular interval, warning message stored/cleared on success - AppStateTests: add warningMessage field to fixture snapshots, new tests for panelContentState_warningWithExistingPRs and healthStatus_warningDoesNotOverrideExistingTriageHealth - GitHubRESTClientPaginationTests: test that search cap stops at page 10 and attaches warning message when totalCount > 1,000 - GitHubRESTClientMergeStateTests: test that resolveDisappearedPRs with unknown mergeableState does not retry when retry is disabled Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- mise.toml: add inline comments clarifying the scope difference between `check` (full verification) and `fix` (auto-fixes only, no tests) - PR ordering plan: guard setOrder to removeObject when ids is empty instead of storing an empty array Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…inject authorizationRequester Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ken and sleep Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rror in health status Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…o 90% capacity Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…andle CI link open errors Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…name Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace direct Task.sleep call with the injected sleep closure so the interval is fully testable without real time passing; add a test that verifies the injected closure receives the correct poll interval. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cap reviewer display at two names and append "and N more" for any extras; extract a shared formattedReviewers helper used by approved, changesRequested, and requested review states. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mark Handler and MockResponse as Sendable and annotate matcher closures with @sendable to satisfy strict concurrency checking. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use exact path matching for pull-details stubs to prevent cross-test interference; rename test to match actual behaviour (returns warning, not throws); rename shadowed local variable in MockGitHubDataSource; loosen AppErrorTests timing and remove locale-sensitive assertion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Complete MVP of GitHub Command Center — a macOS 14+ menu bar app that monitors your GitHub PRs and surfaces what needs your attention.
Core features:
Infrastructure:
Quality:
Test Coverage
AI-assessed coverage: 118/136 code paths tested (87%)
Pre-Landing Review
2 informational issues auto-fixed:
3 informational notes (not blocking):
Adversarial Review
Codex adversarial review found 2 medium-impact items, no blockers:
Scope Drift
Scope Check: CLEAN — all changes directly serve the MVP feature set.
TODOS
No TODO items completed — all 3 items are V1.1+ / V2 post-MVP work.
Test plan
🤖 Generated with Claude Code