Skip to content

ADR-27: Commit-Based Analysis Browsing ​

🇰🇷 한국어 버전

DateAuthorRepos
2026-02-03@KubrickCodeweb

Context ​

The Problem ​

Users could only view the latest analysis results for a repository. However, the database stores multiple analyses tied to specific commit SHAs. After force-push operations (git reset --hard && git push -f), users saw "Up to date" without context, and previously analyzed commits became inaccessible.

User Needs ​

NeedDescription
Historical browsingView analysis results from any previously analyzed commit
Comparison over timeUnderstand how test specifications evolved
Debug regressionsIdentify when specific test specification changes occurred
Shareable analysis stateSend colleagues a link to a specific commit's analysis

Existing Constraints ​

ConstraintSourceImplication
nuqs for URL stateWeb ADR-16Commit selection should use URL parameters
Zustand scopeWeb ADR-26Ephemeral state only; not suitable for navigation
TanStack QueryWeb ADR-04Analysis data fetching pattern established
Next.js App RouterWeb ADR-07SSR considerations for URL state

Decision ​

Adopt URL-based commit selection using nuqs with a CommitSelector UI component.

Core principles:

  1. URL as Source of Truth: Commit selection persists in URL (?commit=abc1234)
  2. Progressive Enhancement: Default to latest commit when no parameter specified
  3. nuqs Integration: Use parseAsString with SHA format validation
  4. Compound State: Combine commit selection with existing filters

URL Structure ​

/repos/{owner}/{repo}/analysis?commit=abc1234
/repos/{owner}/{repo}/analysis?commit=abc1234&view=starred&q=auth

Options Considered ​

Option A: URL-Based Commit Selection via nuqs (Selected) ​

Store selected commit SHA in URL query parameter using the established nuqs pattern.

Pros:

  • Shareable URLs with exact analysis state
  • Browser history integration (back/forward navigation)
  • SSR compatible for pre-fetching
  • Consistent with existing filter patterns (ADR-16)
  • Composable with other URL state

Cons:

  • URL length increase (mitigated by 7-char SHA abbreviation)
  • Invalid commit handling required
  • Stale bookmarks when commits deleted

Option B: Client-Side State via Zustand ​

Store selected commit in Zustand store as ephemeral client state.

Pros:

  • Clean URLs
  • Fast state changes
  • Cross-component access

Cons:

  • Not shareable
  • Lost on page refresh
  • No browser history support
  • Violates ADR-26 scope (Zustand for ephemeral state only)

Rejected: Contradicts established patterns; poor UX for shareable links.

Option C: Path Segment-Based Selection ​

Encode commit SHA in URL path (/analysis/{commit}).

Pros:

  • RESTful semantics
  • Cleaner URL hierarchy

Cons:

  • Routing complexity
  • Inconsistent with query-based filters
  • Page transitions vs. component updates

Rejected: Higher implementation effort; breaks filter composition.

Option D: Dropdown Only (No State Persistence) ​

UI component with local state only.

Rejected: Unacceptable UX; selection lost on any navigation.

Implementation Details ​

API Endpoints ​

EndpointPurpose
GET /api/analyze/{owner}/{repo}/historyList analyzed commits (max 50)
GET /api/analyze/{owner}/{repo}?commit={sha}Get analysis for specific commit

Data Structure ​

typescript
interface AnalysisHistoryItem {
  id: string;
  commitSha: string;
  commitDate: string | null;
  completedAt: string;
  branchName: string | null;
  testCount: number;
  isHead: boolean;
}

URL State Hook ​

typescript
import { parseAsString, useQueryState } from "nuqs";

const commitParser = parseAsString.withDefault("");

export const useCommitSelect = () => {
  const [commit, setCommit] = useQueryState("commit", commitParser);
  const validated = /^[a-f0-9]{7,40}$/.test(commit ?? "") ? commit : null;
  return { commit: validated, setCommit } as const;
};

CommitSelector Component ​

  • Dropdown displaying abbreviated SHA (7 chars)
  • [HEAD] badge for current HEAD commit
  • Shows test count, branch name, date
  • Falls back to static text when < 2 history entries

TanStack Query Integration ​

typescript
export const useAnalysisData = (owner: string, repo: string) => {
  const { commit } = useCommitSelect();

  return useQuery({
    queryKey: ["analysis", owner, repo, commit],
    queryFn: () => fetchAnalysis(owner, repo, commit),
  });
};

Consequences ​

Positive ​

  • Shareable Analysis State: URLs like /analysis?commit=abc1234&view=starred enable collaboration
  • Browser Navigation: Back/forward buttons navigate commit history naturally
  • SSR Optimization: Server can pre-fetch correct analysis from URL parameter
  • Pattern Consistency: Extends nuqs patterns from ADR-16
  • Debuggability: Support can request "send me your URL" for exact state

Negative ​

  • URL Complexity: Longer URLs with commit parameter (mitigated by SHA abbreviation)
  • Invalid State Handling: Must gracefully handle deleted/unavailable commits
  • Query Invalidation: Changing commit invalidates TanStack Query cache (expected behavior)

References ​

Internal ​

External ​

Open-source test coverage insights