Description
Wire GitHubFeature into the main app flow.
(1) Scope in parent reducer (WorktreeFeature)
@ObservableState struct State {
var github: GitHubFeature.State = .init()
}
enum Action {
case github(GitHubFeature.Action)
}
var body: some ReducerOf<Self> {
Scope(state: \.github, action: \.github) { GitHubFeature() }
}
(2) Forward actions
- After
.loadProject loads state.remotes (T-4) → emit .github(.remotesUpdated(map)) where map: [URL: (remote: GitRemote, branch: String)] is built from worktrees × remotes with GitRemote.parse(remote.url), filter nil and host != "github.com".
- On selected worktree change →
.github(.selectedWorktreeChanged(path)).
- NSApp notifications: in MainFeature effects on
.onAppear subscribe to:
NotificationCenter.default.notifications(named: NSApplication.didResignActiveNotification)
NotificationCenter.default.notifications(named: NSApplication.didBecomeActiveNotification)
Emit .github(.windowActiveChanged(active)) accordingly.
(3) Badge in WorktreeRowView
HStack {
// existing: branch name + SHA7 + status badge
Spacer()
if let branchState = githubState(for: worktree.path), isAuthorized {
GitHubBadge(status: branchState.combinedStatus) { showPopover.toggle() }
.popover(isPresented: $showPopover, arrowEdge: .trailing) {
GitHubStatusPopover(
state: popoverState,
onOpenURL: { openURL($0) },
onRefresh: { store.send(.github(.refreshNowTapped(worktree.path))) },
onSignInTapped: { store.send(.github(.signInTapped)) })
}
}
}
@Environment(\.openURL) in App target; bootstrap: @Dependency(\.openURL) = { NSWorkspace.shared.open($0) }.
(4) Accessibility
- Badge — separate focusable element (
accessibilityElement(children: .combine) on row; badge as child with .isButton).
- Popover creates new accessibility context; Esc closes.
(5) Bootstrap
At app launch, credentialStore.currentAccount() → if non-nil, emit bootstrap action calling authService.currentUser() → send .signInResult(.success(info)) on valid. Otherwise start .signedOut. Polling auto-starts from T-8b on .signInResult(.success).
Spec reference
See swarm-report/github-integration-decomposition.md#t-12.
Relationships
Acceptance criteria
Complexity
L
Suggested agent
developer-workflow:swift-engineer (integration) + optionally developer-workflow:swiftui-developer (row UI)
Module / Layer
App target + WorktreeManager + GitHubIntegrationFeature / Wiring
Description
Wire
GitHubFeatureinto the main app flow.(1) Scope in parent reducer (WorktreeFeature)
(2) Forward actions
.loadProjectloadsstate.remotes(T-4) → emit.github(.remotesUpdated(map))wheremap: [URL: (remote: GitRemote, branch: String)]is built from worktrees × remotes withGitRemote.parse(remote.url), filter nil andhost != "github.com"..github(.selectedWorktreeChanged(path))..onAppearsubscribe to:NotificationCenter.default.notifications(named: NSApplication.didResignActiveNotification)NotificationCenter.default.notifications(named: NSApplication.didBecomeActiveNotification)Emit
.github(.windowActiveChanged(active))accordingly.(3) Badge in
WorktreeRowView@Environment(\.openURL)in App target; bootstrap:@Dependency(\.openURL)={ NSWorkspace.shared.open($0) }.(4) Accessibility
accessibilityElement(children: .combine)on row; badge as child with.isButton).(5) Bootstrap
At app launch,
credentialStore.currentAccount()→ if non-nil, emit bootstrap action callingauthService.currentUser()→ send.signInResult(.success(info))on valid. Otherwise start.signedOut. Polling auto-starts from T-8b on.signInResult(.success).Spec reference
See
swarm-report/github-integration-decomposition.md#t-12.Relationships
Acceptance criteria
log stream --subsystem com.relay.github --category polling)state.github.perBranch == [:]Complexity
L
Suggested agent
developer-workflow:swift-engineer(integration) + optionallydeveloper-workflow:swiftui-developer(row UI)Module / Layer
App target +
WorktreeManager+GitHubIntegrationFeature/ Wiring