Files
XCOpenCodeWeb/web/server/lib/github/DOCUMENTATION.md

7.7 KiB

GitHub Module Documentation

Purpose

  • This module owns GitHub auth, Octokit access, repo resolution, and Pull Request status resolution for OpenChamber.
  • From user perspective, this is the layer that lets the app know which PR belongs to a local branch and keeps that UI feeling current.

Entrypoints and structure

  • packages/web/server/lib/github/index.js: public server entrypoint.
  • packages/web/server/lib/github/auth.js: auth storage, multi-account support, client id, scope config.
  • packages/web/server/lib/github/device-flow.js: OAuth device flow.
  • packages/web/server/lib/github/octokit.js: Octokit factory for the current auth.
  • packages/web/server/lib/github/repo/index.js: remote URL parsing and directory-to-repo resolution.
  • packages/web/server/lib/github/pr-status.js: PR lookup across remotes, forks, and upstreams.
  • packages/web/server/index.js: API route layer that calls this module.
  • packages/web/src/api/github.ts: web client wrapper for GitHub endpoints.

Public exports

Auth

  • getGitHubAuth(): current auth entry.
  • getGitHubAuthAccounts(): all configured accounts.
  • setGitHubAuth({ accessToken, scope, tokenType, user, accountId }): save or update account.
  • activateGitHubAuth(accountId): switch active account.
  • clearGitHubAuth(): clear current account.
  • getGitHubClientId(): resolve client id.
  • getGitHubScopes(): resolve scopes.
  • GITHUB_AUTH_FILE: auth file path.

Device flow

  • startDeviceFlow({ clientId, scope }): request device code.
  • exchangeDeviceCode({ clientId, deviceCode }): poll for access token.

Octokit

  • getOctokitOrNull(): current Octokit or null.

Repo

  • parseGitHubRemoteUrl(raw): parse SSH or HTTPS remote URL into { owner, repo, url }.
  • resolveGitHubRepoFromDirectory(directory, remoteName): resolve GitHub repo from a local git remote.

Auth storage and config

  • Auth storage: ~/.config/openchamber/github-auth.json
  • Writes are atomic and file mode is 0o600.
  • Client ID resolution order: OPENCHAMBER_GITHUB_CLIENT_ID -> settings.json -> default.
  • Scope resolution order: OPENCHAMBER_GITHUB_SCOPES -> settings.json -> default.
  • Account id resolution order: explicit accountId -> user login -> user id -> token prefix.

PR integration overview

  • The UI asks github.prStatus(directory, branch, remote?) from packages/web/src/api/github.ts.
  • That hits GET /api/github/pr/status in packages/web/server/index.js.
  • The route calls resolveGitHubPrStatus(...) in packages/web/server/lib/github/pr-status.js.
  • The resolver finds the most likely repo and PR for a local branch.
  • The route then enriches that result with checks, mergeability, and permission-related fields.
  • The client caches and shares the result between sidebar and Git view.

Consumers of PR data

  • packages/ui/src/components/session/SessionSidebar.tsx reads all PR entries and maps them to directory::branch.
  • packages/ui/src/components/session/sidebar/SessionGroupSection.tsx renders the compact badge, PR number, title, checks summary, and GitHub link.
  • packages/ui/src/components/views/git/PullRequestSection.tsx uses the same shared entry for the full PR workflow.
  • packages/ui/src/components/ui/MemoryDebugPanel.tsx reads request counters for debugging.

How PR resolution works

  • It reads local git status and remotes first.
  • It ranks remotes in this order: explicit remote, tracking remote, origin, upstream, then the rest.
  • It resolves those remotes into GitHub repos.
  • It expands each repo through parent and source so PRs in upstream repos can still be found.
  • It skips PR lookup when the current branch matches that repo's default branch.
  • It first searches for PRs by likely source owner plus exact head branch.
  • If that fails, it falls back to broader GitHub search for the branch name.
  • 403 and 404 during repo lookups are treated as expected gaps, not hard errors.

Shared client state model

  • Client key is effectively directory::branch.
  • One entry stores last known status, loading state, error, timestamps, watcher count, identity, and resolved remote.
  • Requests are deduplicated by branch signature, not by component instance.
  • This keeps sidebar and Git view aligned and avoids duplicated fetches.

Persistence

  • PR state is persisted in local storage under openchamber.github-pr-status.
  • Persisted fields include status, timestamps, identity, and resolved remote.
  • Runtime-only details are not persisted.
  • Persisted entries expire after 12 hours.
  • On reload, users get last known state first, then background refresh resumes.

Polling and refresh model

  • There are two layers: entry-level polling in useGitHubPrStatusStore and repo scanning in useGitHubPrBackgroundTracking.
  • Entry-level polling decides when a known branch should revalidate PR state.
  • Background tracking decides which directories and branches should even be watched.

Entry-level polling rules

  • Start watching -> immediate refresh.
  • If no PR is found yet -> retry after 2s and 5s.
  • Still no PR -> discovery refresh every 5m.
  • Open PR with pending checks -> refresh about every 1m.
  • Open PR with non-pending checks -> refresh about every 5m.
  • Open PR without a stable checks signal -> refresh about every 2m.
  • Closed or merged PR -> stop regular polling.
  • Hidden tab -> skip polling.
  • Non-forced refreshes use a 90s TTL.

Background tracking rules

  • Track up to 50 likely directories.
  • Sources are current directory, projects, worktrees, active sessions, and archived sessions.
  • Active directory branch TTL is 15s.
  • Background directory branch TTL is 2m.
  • Background scan wakes every 15s, but only fetches directories whose TTL expired.
  • Each scan reads branch, tracking, ahead, and behind from git status.
  • If any of those branch signals change, that branch's PR status refreshes immediately.
  • After that, one more delayed refresh runs after 5s to catch GitHub eventual consistency.

UI refresh triggers

  • App or tab becomes visible.
  • Window regains focus.
  • Current branch changes.
  • Tracking branch changes.
  • Ahead or behind changes.
  • User selects a different remote in Git view.
  • GitHub auth state changes.

Action-based refreshes in Git view

  • After Create PR -> refresh now, then after 2s and 5s.
  • After Merge PR -> refresh now, then after 2s and 5s.
  • After Mark ready for review -> refresh now, then after 2s and 5s.
  • After Update PR -> refresh now, then after 2s and 5s.

Sidebar behavior

  • Sidebar shows only compact PR state.
  • Aggregation is by directory::branch, so multiple sessions on one branch share one signal.
  • If multiple entries exist, sidebar keeps the strongest visible PR state.
  • Visual state is based on PR health, not merge permissions.

Git view behavior

  • Git view watches one branch directly.
  • It supports create, edit, mark ready, and merge.
  • It can probe alternate remotes so fork-heavy setups still find the right PR.
  • It uses the same shared store as the sidebar.

Failure handling

  • If GitHub is disconnected, API returns connected: false.
  • If a repo is private or inaccessible, resolver calls may quietly return no PR.
  • Sidebar stays quiet on missing or inaccessible PR state.
  • Git view is where explicit PR-level problems should be shown.

Notes for contributors

  • Keep the UI calm. Do not add noisy diagnostics to the sidebar.
  • Prefer shared state over per-component fetches.
  • Prefer event-shaped refreshes over blind frequent polling.
  • Prefer correctness for fork and multi-remote setups over assuming origin is enough.
  • Device flow handles GitHub authorization_pending at caller level.
  • Repo parser supports git@github.com:, ssh://git@github.com/, and https://github.com/.