From fe508767f432f5edb2476b8307d1c325f3d0c44d Mon Sep 17 00:00:00 2001 From: Ritikydv1 Date: Thu, 23 Jul 2026 00:40:46 +0530 Subject: [PATCH 1/2] docs: add agent.md --- agent.md | 374 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 agent.md diff --git a/agent.md b/agent.md new file mode 100644 index 0000000..48fb0d2 --- /dev/null +++ b/agent.md @@ -0,0 +1,374 @@ +# AGENTS.md + +This file gives AI coding agents (Claude, Copilot, Cursor, etc.) the context needed +to work on OrgExplorer without re-deriving architecture from scratch. Read this +before making changes. + +## What this project is + +OrgExplorer is a dashboard for exploring GitHub organizations — repositories, +contributors, governance health, network graphs, and activity trends. It's a +React + Vite frontend that talks directly to the GitHub REST API from the +browser (no backend server). + +## Project Philosophy + +This project originates from AOSSIE's GSoC idea proposal (see +[project brief link/reference]), which set three hard requirements: + +- No backend — must run entirely in the browser. +- Local storage / IndexedDB only for persistence. +- Aggressive local caching to avoid exceeding GitHub API limits. + +Everything else in this codebase is downstream of those three constraints. +OrgExplorer prioritizes: + +1. **GitHub API efficiency over completeness when unauthenticated.** Standard + mode exists because the app has no server to proxy requests through or + hide a shared token behind — every unauthenticated visitor shares GitHub's + 60 req/hr limit individually. Scoping to top-10-per-org is a deliberate + trade-off, not a shortcut to remove later. +2. **Browser-only architecture (no backend).** Do not introduce a server, + serverless function, or proxy to work around rate limits or CORS — solve + it client-side (PAT support, caching, batching) or don't solve it. +3. **Derived analytics instead of raw GitHub listings.** Prefer computing a + health score, ranking, or trend over just displaying a raw API response. + The value of this app is the synthesis, not the listing. +4. **Deterministic and explainable metrics.** Every score (health score, + repo ranking, bus factor, etc.) should be traceable to a documented + formula — no black-box weighting, no randomness, no metric a user can't + get an explanation for via a "Learn More"-style modal. +5. **Fast repeat visits through aggressive caching.** IndexedDB caching + (`services/cache.js`, 1hr TTL) exists specifically so a user re-opening + the same org doesn't re-spend their rate limit. Any new fetch path should + go through `fetchWithCache`, not a raw `fetch()`. + +When multiple implementations are possible, prefer the one that minimizes +API requests while keeping analytics deterministic. + +## Existing Libraries + +React, Vite, TailwindCSS, Recharts, D3, React Router. + +Prefer these existing libraries before introducing a new dependency. If you +believe a new library is genuinely needed, say so explicitly in the PR +description rather than adding it silently. + +Note: `services/cache.js` uses the native browser `indexedDB` API directly +(not the `idb` npm wrapper). Do not add `idb` as a dependency to "simplify" +this — it would mean rewriting working, tested code for no functional gain. + +## Core constraint: GitHub API rate limits + +This shapes almost every architectural decision in the codebase. Unauthenticated +requests are capped at 60/hr; requests with a Personal Access Token (PAT) get +5,000/hr. Because of this, the app runs in one of two modes: + +- **Standard mode (no PAT)** — analyzes a scoped-down subset of the org to stay + within the unauthenticated limit. +- **Complete mode (PAT connected)** — analyzes the full org. + +Every data-fetching function needs to respect this distinction. Do not add a +fetch that ignores the `pat` presence check. + +## Repo-scoping rule (the most important pattern in this codebase) + +```javascript +// services/analytics.js +export function getTopRepositories(repos, limit = 10) { + // Score = stars + 2×forks + 1.5×watchers + recency bonus + // (bonus decays linearly for repos not pushed to within the last year) +} +``` + +```javascript +// context/AppContext.jsx +const selectAnalysisRepos = useCallback((allRepos) => { + const byOrg = {} + for (const repo of allRepos) { + (byOrg[repo.orgLogin] ??= []).push(repo) + } + return Object.values(byOrg).flatMap(orgRepos => + pat ? orgRepos : getTopRepositories(orgRepos, 10) + ) +}, [pat]) +``` + +**Rule: without a PAT, only the top 10 repos per org (by this scoring formula) +are used for contributors, issues, and pull requests. With a PAT, all fetched +repos per org are used.** + +If you add a new feature that fetches per-repo data (e.g. a new metric type), +it must go through `selectAnalysisRepos` (or an equivalent that applies the +same rule) — not `model.totalRepos` directly, and not a different top-N cutoff. +Inconsistent scoping across features is a recurring bug class in this repo; +see git history around "PR-metrics scoping" for a real example. + +## Derived Metrics + +- Health Score +- Activity Classification (repo-level; previously called "lifecycle" in + some older code/tooltips — that naming is deprecated, use + `activityClassification`) +- Bus Factor +- Contributor Freshness +- Issue Resolution Rate +- Cross-repo contributor signal + +These metrics are deterministic — same inputs always produce the same +output, no randomness, no hidden weighting. + +Never modify a metric's formula without updating both its documentation +here and every page that consumes it. + +Note: if additional metrics exist in the codebase beyond this list, add them here with their formula reference +— don't let an agent infer meaning from variable names alone. + +## Stable Contracts + +Unless explicitly requested, do not change: + +- `fetchWithCache()` +- `buildAnalyticalModel()` +- `AppContext` state shape +- PAT save/load flow +- Cache TTL (1 hour) +- Health score formula +- `getTopRepositories()` / `selectAnalysisRepos()` scoring and scoping logic + +These are load-bearing for every page in the app. A change here has a wide +blast radius — treat it as an architecture change, not a local fix. + +## Fetch caps by data type + +| Data | No PAT | With PAT | +|---|---|---| +| Repos fetched (`fetchRepos`) | ≤500/org (5 pages) | all repos/org (`Math.ceil(public_repos/100)` pages) | +| Repos used downstream | top 10/org | all fetched repos/org | +| Contributors/repo | ≤100 (1 page) | ≤1000 (10 pages) | +| Issues/repo | ≤100 (1 page) | ≤1000 (10 pages) | +| Pull Requests/repo | ≤100 (1 page) | ≤1000 (10 pages) | + +These caps live in `services/github.js` inside each `fetchX` function as +`maxPages = pat ? 10 : 1` (or `5` for the initial repo list). Keep them +symmetric across `fetchContributors`, `fetchIssues`, and `fetchPulls` unless +there's a specific reason to diverge — they were intentionally aligned. + +## State architecture (`context/AppContext.jsx`) + +Single context provider (`AppProvider`) holds all cross-page state. Key pieces: + +- **`model`** — built by `buildAnalyticalModel()`, contains `allRepos` + (PAT-scoped, used for contributors) and `totalRepos` (raw, unscoped fetch — + use this + `selectAnalysisRepos` for new per-repo fetches, not `allRepos`). +- **`isComplete`** — true once org metadata + all repos + all contributors are + fetched (i.e. `explore()` ran with a PAT). Overview, Contributors, + Repositories, and Network pages gate their "Run Complete Analysis" banner on + this flag. +- **`auditComplete`** — true once issues are fetched for the full repo set. + Used by the issue/governance side of Analytics and Governance pages. +- **`advanceAnalyticsComplete`** — same idea, for pull-request data. +- **`lastOrgNames`** — remembers the last searched org(s) so any page can + re-trigger `explore()` without the user re-entering names. + +## Security + +Handling of the GitHub PAT: + +- Never log the PAT, in console output, error messages, or analytics. +- Never send the PAT anywhere except directly to `api.github.com`. +- Never persist the PAT outside `localStorage` (no cookies, no query + params, no external storage). +- Never expose the PAT in a URL, including in `Link` headers or debug + output. + +Note: the PAT is currently stored as plaintext in `localStorage` +(`oe_pat`), which is readable by any script able to execute in this origin. +This is a known, accepted trade-off for a backend-less app — do not "fix" +it by adding a server or remote storage without discussing it first, since +that would violate the no-backend requirement. + +### Entry points and when to use which + +| Function | Fetches | Use when | +|---|---|---| +| `explore(orgNames)` | org metadata, repos, contributors | Initial search | +| `runFullExplore()` | same as `explore`, replays `lastOrgNames` | Any page's banner needs to (re-)run a full analysis | +| `runAudit()` | issues only, from current `model` | Rare — most callers should use `runGovernanceAnalysis` instead | +| `runGovernanceAnalysis()` | repos (if needed) + issues | Governance page trigger | +| `runAdvanceAnalytics()` | repos (if needed) + pulls | Analytics page (PR metrics) trigger, no-PAT case | +| `runFullAnalytics()` | repos (if needed) + issues + pulls (parallel) | Analytics page combined trigger, used once PAT is connected | + +**Pattern to follow for any new "Run Complete Analysis" feature:** + +```javascript +const runSomeNewAnalysis = useCallback(async () => { + if (someLoadingFlag) return + + let currentModel = model + if (!isComplete) { + setSomeLoadingFlag(true) + const freshModel = await runFullExplore() + setSomeLoadingFlag(false) + if (!freshModel) return + currentModel = freshModel + } + + if (!currentModel) return + + setSomeLoadingFlag(true) + const repos = selectAnalysisRepos(currentModel.totalRepos) + // ...fetch and process... + setSomeLoadingFlag(false) + setSomeCompleteFlag(!!pat) +}, [isComplete, model, runFullExplore, selectAnalysisRepos, pat, someLoadingFlag]) +``` + +This avoids a real stale-closure bug that's bitten this codebase before: don't +read `model` from context state right after calling `runFullExplore()` — state +updates aren't synchronous, so use the model **returned** by the explore call +instead. + +## `AnalysisBanner` component + +Shared banner shown at the top of Overview, Repositories, Contributors, +Network, Governance, and Analytics. It: + +- Shows "Standard" vs "Complete" status via an `analysisStatus` prop + (`'standard' | 'complete'`). +- On click, checks if a PAT is saved; if not, opens `PATModal` instead of + running anything. +- Calls the page's `onRun` callback (one of the `runX` functions above) once a + PAT exists. + +When adding this banner to a new page, wire `analysisStatus` to whichever +`*Complete` flag actually reflects that page's data — don't reuse +`isComplete` for pages whose data depends on a *later* fetch stage (issues, +pulls) that could still be standard-scoped even after `isComplete` is true. + +## UI Guidelines + +- Reuse existing shared components (`AnalysisBanner`, `PATModal`, card/stat + components in `components/UI`) rather than building page-specific + one-offs. +- Reuse existing skeleton loaders (e.g. `OverviewSkeleton`) rather than + introducing a new loading pattern per page. +- Every page should account for all five states: loading, empty, error, + standard mode, and complete mode. Don't ship a page that only handles the + happy path. +- Loading checks must run before any early `return null` for missing data + — see "Common bugs" below on hook ordering. + +## Accessibility + +Maintain across all UI work: + +- Keyboard navigation for interactive elements (modals, dropdowns, buttons) +- Semantic HTML over div-soup where practical +- Visible focus states +- `aria-label`s on icon-only buttons +- Sufficient color contrast against the existing dark theme + +Note: as of the current codebase, `PATModal` is missing dialog semantics +(`role="dialog"`, `aria-modal`, focus trap, Escape-to-close) — this is a +known gap, not a pattern to replicate in new modals. + +## Caching + +`services/cache.js` wraps every GitHub request in an IndexedDB cache +(`fetchWithCache`), TTL 1 hour. Before adding a "refetch" trigger, check +whether the data is already cached — a complete-analysis re-run should not +redundantly refetch repos/contributors if `isComplete` is already `true`. + +## Error Handling + +GitHub API errors should always be classified into one of: + +- Rate limit +- Network failure +- Authentication (invalid/expired PAT) +- Not Found +- Unknown + +Never surface a raw GitHub API error message directly to the user — map it +to one of the categories above and show a clear, actionable message (see +the existing `RATE_LIMIT` handling in `explore()` for the pattern to follow). + +## Common bugs seen in this codebase (avoid repeating them) + +1. **Hooks after an early return.** Several pages do `if (!model) return null` + near the top. Any `useMemo`/`useCallback`/custom hook must be called + *before* that line, or React throws "Rendered fewer hooks than expected." +2. **Argument-order mismatches.** `fetchRepos(org, repoCount, pat)` has been + miscalled as `fetchRepos(org.login, pat)` before, silently breaking PAT + detection. Double-check parameter order against the function signature, + not just the call site that "looks right." +3. **Clearing state synchronously at the start of a re-fetch.** `explore()` + used to do `setModel(null)` immediately on every call, which unmounted + pages guarded by `if (!model) return null` and caused a visible flicker. + Prefer swapping state atomically once new data is ready, so the previous + view stays visible during a refresh. +4. **Reusing standard-mode dropdowns/lists without checking what was actually + fetched.** Derive UI lists (e.g. repo-select dropdowns) from the fetched + data map (`Object.keys(issuesData)`) rather than from `model.totalRepos`, + or they'll show entries with no underlying data when in standard mode. + +## Avoid Duplication + +Do not duplicate: + +- Analytics formulas (health score, repo ranking, freshness, etc.) +- Fetch logic (`fetchWithCache` and the `fetchX` functions in `services/github.js`) +- Cache logic (`services/cache.js`) +- PAT detection/branching (`pat ? ... : ...`) +- Repository scoping/selection (`selectAnalysisRepos`, `getTopRepositories`) + +If you find yourself re-implementing any of the above inline in a page or +component, stop and import the existing utility instead. Several bugs in +this codebase's history came from exactly this — a feature independently +re-deriving repo selection or fetch logic slightly differently than the +rest of the app, causing silent data mismatches between pages. + +## Where things live + +- `context/AppContext.jsx` — all shared state and data-fetching orchestration +- `services/github.js` — raw GitHub API calls, PAT-aware pagination +- `services/analytics.js` — `buildAnalyticalModel`, `getTopRepositories`, `time-series builders` +- `services/cache.js` — IndexedDB L2 cache wrapper +- `components/AnalysisBanner.jsx` — shared standard/complete banner + PAT gate +- `components/PATModal.jsx` — PAT entry/save/delete UI +- `components/LearnMoreModal.jsx` — explains standard vs. complete analysis limits +- `pages/*.jsx` — Overview, Repositories, Contributors, Network, Governance, + Analytics, Settings + +## Before submitting a change + +- If you touched fetch logic, re-check the fetch-cap table above stays + consistent across contributors/issues/pulls. +- If you touched a page's loading/empty state, check whether a sibling + section on the same page has an equivalent state that should match (see + the Activity Trends / Advanced Analytics consistency pass in git history + for the expected pattern). +- Don't hand-roll a new top-N repo cutoff — use `selectAnalysisRepos` / + `getTopRepositories`. + +## Testing + +Every new feature should include: + +- Utility/unit tests for pure functions (`services/analytics.js`, + `services/github.js` helpers) +- Component tests where behavior is nontrivial (not just presence) + +Avoid snapshot-only tests. Prefer tests that assert on actual behavior +(e.g. "given these repos and no PAT, the top 10 by score are selected") +over tests that just lock in current output. + +## When in Doubt + +Prefer consistency over cleverness. + +Reuse existing hooks and components before writing new ones. + +Ask before changing core architecture (see Stable Contracts above) rather +than assuming a refactor is welcome. \ No newline at end of file From 9e80e96d68d7d5f1106f239dae8da05ae119aa80 Mon Sep 17 00:00:00 2001 From: Ritikydv1 Date: Thu, 23 Jul 2026 00:45:41 +0530 Subject: [PATCH 2/2] docs: update agent.md adding few more headings --- agent.md | 90 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/agent.md b/agent.md index 48fb0d2..183fda4 100644 --- a/agent.md +++ b/agent.md @@ -14,7 +14,7 @@ browser (no backend server). ## Project Philosophy This project originates from AOSSIE's GSoC idea proposal (see -[project brief link/reference]), which set three hard requirements: +[https://github.com/AOSSIE-Org/OrgExplorer]), which set three hard requirements: - No backend — must run entirely in the browser. - Local storage / IndexedDB only for persistence. @@ -121,8 +121,9 @@ output, no randomness, no hidden weighting. Never modify a metric's formula without updating both its documentation here and every page that consumes it. -Note: if additional metrics exist in the codebase beyond this list, add them here with their formula reference -— don't let an agent infer meaning from variable names alone. +Note: if additional metrics exist in the codebase beyond this list, add them +here with their formula reference — don't let an agent infer meaning from +variable names alone. ## Stable Contracts @@ -171,24 +172,7 @@ Single context provider (`AppProvider`) holds all cross-page state. Key pieces: - **`lastOrgNames`** — remembers the last searched org(s) so any page can re-trigger `explore()` without the user re-entering names. -## Security - -Handling of the GitHub PAT: - -- Never log the PAT, in console output, error messages, or analytics. -- Never send the PAT anywhere except directly to `api.github.com`. -- Never persist the PAT outside `localStorage` (no cookies, no query - params, no external storage). -- Never expose the PAT in a URL, including in `Link` headers or debug - output. - -Note: the PAT is currently stored as plaintext in `localStorage` -(`oe_pat`), which is readable by any script able to execute in this origin. -This is a known, accepted trade-off for a backend-less app — do not "fix" -it by adding a server or remote storage without discussing it first, since -that would violate the no-backend requirement. - -### Entry points and when to use which +## Entry points and when to use which | Function | Fetches | Use when | |---|---|---| @@ -273,6 +257,23 @@ Note: as of the current codebase, `PATModal` is missing dialog semantics (`role="dialog"`, `aria-modal`, focus trap, Escape-to-close) — this is a known gap, not a pattern to replicate in new modals. +## Security + +Handling of the GitHub PAT: + +- Never log the PAT, in console output, error messages, or analytics. +- Never send the PAT anywhere except directly to `api.github.com`. +- Never persist the PAT outside `localStorage` (no cookies, no query + params, no external storage). +- Never expose the PAT in a URL, including in `Link` headers or debug + output. + +Note: the PAT is currently stored as plaintext in `localStorage` +(`oe_pat`), which is readable by any script able to execute in this origin. +This is a known, accepted trade-off for a backend-less app — do not "fix" +it by adding a server or remote storage without discussing it first, since +that would violate the no-backend requirement. + ## Caching `services/cache.js` wraps every GitHub request in an IndexedDB cache @@ -333,7 +334,7 @@ rest of the app, causing silent data mismatches between pages. - `context/AppContext.jsx` — all shared state and data-fetching orchestration - `services/github.js` — raw GitHub API calls, PAT-aware pagination -- `services/analytics.js` — `buildAnalyticalModel`, `getTopRepositories`, `time-series builders` +- `services/analytics.js` — `buildAnalyticalModel`, `getTopRepositories`, time-series builders - `services/cache.js` — IndexedDB L2 cache wrapper - `components/AnalysisBanner.jsx` — shared standard/complete banner + PAT gate - `components/PATModal.jsx` — PAT entry/save/delete UI @@ -341,20 +342,27 @@ rest of the app, causing silent data mismatches between pages. - `pages/*.jsx` — Overview, Repositories, Contributors, Network, Governance, Analytics, Settings -## Before submitting a change +## Performance Constraints -- If you touched fetch logic, re-check the fetch-cap table above stays - consistent across contributors/issues/pulls. -- If you touched a page's loading/empty state, check whether a sibling - section on the same page has an equivalent state that should match (see - the Activity Trends / Advanced Analytics consistency pass in git history - for the expected pattern). -- Don't hand-roll a new top-N repo cutoff — use `selectAnalysisRepos` / - `getTopRepositories`. +Target: + +- Initial load < 3 seconds +- Cached navigation < 100ms +- Network graph should remain responsive above 1,000 edges +- Zero unnecessary re-renders + +Avoid: + +- Nested loops over repositories where a single pass would do +- Duplicate API requests for data already in `model` or the IndexedDB cache +- Rebuilding graphs/models on every render instead of memoizing ## Testing -Every new feature should include: +No automated test suite currently exists in this codebase. This section +applies once one is introduced. + +When it is, every new feature should include: - Utility/unit tests for pure functions (`services/analytics.js`, `services/github.js` helpers) @@ -364,6 +372,22 @@ Avoid snapshot-only tests. Prefer tests that assert on actual behavior (e.g. "given these repos and no PAT, the top 10 by score are selected") over tests that just lock in current output. +## Before submitting a change + +- Keep PRs focused — avoid unrelated refactors bundled into a feature or fix. +- If you touched fetch logic, re-check the fetch-cap table above stays + consistent across contributors/issues/pulls. +- If you touched a page's loading/empty state, check whether a sibling + section on the same page has an equivalent state that should match (see + the Activity Trends / Advanced Analytics consistency pass in git history + for the expected pattern). +- Update this file if the architecture, stable contracts, or metrics + formulas change. +- Buttons should have explicit `type="button"` unless they're meant to + submit a form (rare in this codebase). +- Don't hand-roll a new top-N repo cutoff — use `selectAnalysisRepos` / + `getTopRepositories`. + ## When in Doubt Prefer consistency over cleverness. @@ -371,4 +395,4 @@ Prefer consistency over cleverness. Reuse existing hooks and components before writing new ones. Ask before changing core architecture (see Stable Contracts above) rather -than assuming a refactor is welcome. \ No newline at end of file +than assuming a refactor is welcome.