{"id":49495948,"url":"https://github.com/sjquant/orgpulse","last_synced_at":"2026-05-01T09:07:57.424Z","repository":{"id":351820745,"uuid":"1211528230","full_name":"sjquant/orgpulse","owner":"sjquant","description":"Generate GitHub organization metrics snapshots, repo rollups, and summaries.","archived":false,"fork":false,"pushed_at":"2026-04-27T15:08:02.000Z","size":446,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-27T15:23:23.032Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sjquant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-15T13:35:33.000Z","updated_at":"2026-04-21T14:26:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sjquant/orgpulse","commit_stats":null,"previous_names":["sjquant/orgpulse"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sjquant/orgpulse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjquant%2Forgpulse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjquant%2Forgpulse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjquant%2Forgpulse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjquant%2Forgpulse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sjquant","download_url":"https://codeload.github.com/sjquant/orgpulse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjquant%2Forgpulse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32490872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-01T09:07:57.363Z","updated_at":"2026-05-01T09:07:57.417Z","avatar_url":"https://github.com/sjquant.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# orgpulse\n\nGitHub organization metrics snapshots and rollups.\n\n`orgpulse` collects pull request activity across every repository in a GitHub\norganization, builds repo-level and org-level summaries, and writes stable file\noutputs for historical tracking.\n\n## What It Does\n\n- Collects PR, review, and merge data across an organization\n- Generates repo-level and org-level metrics\n- Writes normalized raw snapshots plus summary outputs to files\n- Locks closed reporting periods so historical results stay stable\n- Supports `full`, `incremental`, and `backfill` run modes\n\n## Prerequisites\n\n- Python `3.11+`\n- `uv`\n- GitHub credentials that can read the target organization\n\nInstall the runtime dependencies from the repo root:\n\n```bash\nuv sync\n```\n\nInstall contributor tooling if you also want linting and tests:\n\n```bash\nuv sync --group dev\n```\n\n## Authentication\n\n`orgpulse` resolves GitHub credentials in this order:\n\n1. `GH_TOKEN`\n2. `gh auth token` from an existing GitHub CLI login\n\nExample with an environment token:\n\n```bash\nexport GH_TOKEN=ghp_your_token_here\nuv run orgpulse run --org acme\n```\n\nExample with GitHub CLI auth:\n\n```bash\ngh auth login\nuv run orgpulse run --org acme\n```\n\nBefore collection starts, `orgpulse` validates that the resolved credentials can\naccess the target organization.\n\n## CLI Contract\n\n`orgpulse run` accepts these operator-facing options:\n\n- `--org \u003cslug\u003e`: target GitHub organization. Falls back to `ORGPULSE_ORG`.\n- `--as-of \u003cYYYY-MM-DD\u003e`: anchor date used to resolve the current open reporting\n  period. Falls back to `ORGPULSE_AS_OF` or today.\n- `--period \u003cmonth|week\u003e`: reporting grain. Falls back to `ORGPULSE_PERIOD`.\n- `--mode \u003cfull|incremental|backfill\u003e`: run strategy. Falls back to\n  `ORGPULSE_MODE`.\n- `--repo \u003cname-or-org/name\u003e`: include only matching repositories. Repeatable.\n- `--exclude-repo \u003cname-or-org/name\u003e`: exclude matching repositories.\n  Repeatable.\n- `--output-dir \u003cpath\u003e`: output root. Falls back to `ORGPULSE_OUTPUT_DIR`.\n- `--backfill-start \u003cYYYY-MM-DD\u003e` and `--backfill-end \u003cYYYY-MM-DD\u003e`: required\n  together for `--mode backfill`.\n\nNotes:\n\n- `--repo` and `--exclude-repo` cannot overlap.\n- If a repo filter is owner-qualified, its owner must match `--org`.\n- `orgpulse` writes a JSON run summary to stdout and writes period files under\n  `--output-dir`.\n\n## Run Modes\n\n### Incremental\n\n`incremental` is the default and is the normal operator mode.\n\n- Refreshes only the current open period.\n- Reuses locked closed periods from the existing manifest when the run contract\n  matches the same org, period grain, repo filters, and output root.\n- Leaves locked historical raw snapshots and summaries untouched.\n- Promotes a previously refreshed open period into locked history after that\n  period closes on a later run.\n\nExample:\n\n```bash\nuv run orgpulse run \\\n  --org acme \\\n  --period month \\\n  --mode incremental \\\n  --as-of 2026-04-18 \\\n  --output-dir output\n```\n\n### Full\n\n`full` rebuilds the full discovered history up to `--as-of`.\n\n- Ignores locked-period skipping.\n- Rewrites refreshed periods from scratch.\n- Prunes stale period directories that no longer belong to the rebuilt history.\n- Use it when you want to replace the current snapshot set instead of preserving\n  prior locked history.\n\nExample:\n\n```bash\nuv run orgpulse run \\\n  --org acme \\\n  --period month \\\n  --mode full \\\n  --as-of 2026-04-18 \\\n  --output-dir output\n```\n\n### Backfill\n\n`backfill` recalculates an explicit closed-period range without rebuilding the\nentire history.\n\n- Requires both `--backfill-start` and `--backfill-end`.\n- Both dates must align to the selected period boundary.\n- The backfill end date must be before the current open period begins, as\n  defined by `--as-of`.\n- Rewrites only the requested closed periods and preserves unrelated locked\n  history.\n- Writes header-only raw CSVs and zero-valued summaries when a requested period\n  has no matching pull requests.\n\nMonthly backfill example:\n\n```bash\nuv run orgpulse run \\\n  --org acme \\\n  --period month \\\n  --mode backfill \\\n  --as-of 2026-05-18 \\\n  --backfill-start 2026-03-01 \\\n  --backfill-end 2026-04-30 \\\n  --output-dir output\n```\n\nWeekly backfill example:\n\n```bash\nuv run orgpulse run \\\n  --org acme \\\n  --period week \\\n  --mode backfill \\\n  --as-of 2026-05-18 \\\n  --backfill-start 2026-04-06 \\\n  --backfill-end 2026-04-19 \\\n  --output-dir output\n```\n\n## Practical Operator Workflow\n\nUse `incremental` for routine scheduled runs. Use `backfill` when you need to\nrepair or refresh one or more closed periods. Use `full` when you intentionally\nwant to replace the currently materialized history.\n\nExamples with repo filters:\n\n```bash\nuv run orgpulse run \\\n  --org acme \\\n  --mode incremental \\\n  --repo api \\\n  --repo web \\\n  --exclude-repo legacy \\\n  --output-dir output\n```\n\nEnvironment-backed defaults can remove repeated flags:\n\n```bash\nexport ORGPULSE_ORG=acme\nexport ORGPULSE_PERIOD=month\nexport ORGPULSE_MODE=incremental\nexport ORGPULSE_OUTPUT_DIR=output\nuv run orgpulse run --as-of 2026-04-18\n```\n\n## Locked-Period Behavior\n\n`orgpulse` treats the current open period as mutable and closed periods as\nstable history.\n\n- Incremental runs skip locked periods and refresh only the open period.\n- Full and backfill runs refresh locked periods instead of skipping them.\n- Closed periods become locked after a successful run.\n- Locked periods are carried forward only when the saved manifest still matches\n  the same org, period grain, repo filters, and raw snapshot root.\n- If the saved manifest contract does not match, `orgpulse` does not reuse those\n  historical locks.\n\nThis keeps normal runs diff-friendly while still allowing explicit historical\nrepair when needed.\n\n## Output Layout\n\nFor `--output-dir output --period month`, the generated layout is:\n\n```text\noutput/\n  raw/month/\n    2026-04/\n      pull_requests.csv\n      pull_request_reviews.csv\n      pull_request_timeline_events.csv\n  manifest/month/\n    manifest.json\n    index.json\n    README.md\n  repo_summary/month/\n    contract.json\n    index.json\n    README.md\n    latest/\n      repo_summary.csv\n    2026-04/\n      repo_summary.csv\n  org_summary/month/\n    contract.json\n    index.json\n    README.md\n    latest/\n      summary.json\n      summary.md\n    2026-04/\n      summary.json\n      summary.md\n```\n\n## Local Analysis\n\n`orgpulse analyze` reads the local snapshot and manifest outputs and builds\nfocused analysis views without refetching GitHub data.\n\n- Supports `period`, `repository`, and `author` groupings\n- Respects `--since`, `--until`, `--time-anchor`, `--top`, and\n  `--distribution-percentile`\n- Writes JSON, CSV, Markdown, or interactive HTML to stdout\n- Trims upper-tail outliers from distribution-based metrics with\n  `--distribution-percentile 95|99|100` where `100` keeps all values\n- HTML output includes shared controls, single-series focus mode, and spike\n  diagnostics such as same-period-created ratio, older-PR ratio, top\n  contributing repositories, top updated dates, and timeline-event breakdowns\n\n`orgpulse dashboard` reads local `month/created_at` outputs and renders the\nsupported dashboard view as JSON, per-PR CSV, and interactive HTML files.\n\n- Reads local manifest-backed raw snapshots instead of refetching full history\n- Currently supports only `month` grain with the `created_at` anchor\n- Respects `--since`, `--until`, `--distribution-percentile`, and\n  `--refresh/--no-refresh`\n- Writes dashboard artifacts under an explicit `--output-dir`\n- Reuses the saved local manifest contract from `--source-output-dir`\n\n`orgpulse dashboard-render` re-renders HTML from an existing dashboard JSON\npayload without requiring manifest-backed raw snapshots.\n\nExample HTML analysis:\n\n```bash\nuv run orgpulse analyze \\\n  --org acme \\\n  --grain month \\\n  --group-by repository \\\n  --time-anchor updated_at \\\n  --since 2026-04-01 \\\n  --until 2026-04-30 \\\n  --distribution-percentile 95 \\\n  --format html \\\n  --output-dir output \u003e analysis.html\n```\n\nExample dashboard render:\n\n```bash\nuv run orgpulse dashboard \\\n  --org acme \\\n  --since 2026-01-01 \\\n  --until 2026-04-27 \\\n  --source-output-dir output \\\n  --output-dir output/acme-review/manual-2026-04-27 \\\n  --distribution-percentile 99\n```\n\nExample render-only refresh:\n\n```bash\nuv run orgpulse dashboard-render \\\n  --input-json output/acme-review/manual-2026-04-27/acme-created-at-since-2026-01-01.json \\\n  --output-html output/acme-review/manual-2026-04-27/acme-created-at-since-2026-01-01.html \\\n  --distribution-percentile 99\n```\n\n### Raw snapshots\n\n- `raw/\u003cgrain\u003e/\u003cperiod\u003e/pull_requests.csv`: normalized PR rows\n- `raw/\u003cgrain\u003e/\u003cperiod\u003e/pull_request_reviews.csv`: normalized review rows\n- `raw/\u003cgrain\u003e/\u003cperiod\u003e/pull_request_timeline_events.csv`: timeline events used\n  for review timing and state transitions\n\n### Manifest\n\n- `manifest.json`: latest run metadata, including refreshed periods, locked\n  periods, and watermarks\n- `index.json`: machine-readable index with the latest run metadata plus history\n  lists for refreshed and locked periods\n- `README.md`: human-readable manifest index\n\nThe manifest watermarks track:\n\n- `collection_window_start_date`\n- `collection_window_end_date`\n- `latest_refreshed_period_end_date`\n- `latest_locked_period_end_date`\n\n### Repo summaries\n\n- `\u003cperiod\u003e/repo_summary.csv`: repo-level rollup for that period\n- `latest/repo_summary.csv`: convenience copy of the newest period summary\n- `index.json`: machine-readable map of the latest summary and all saved history\n- `README.md`: human-readable history table\n- `contract.json`: run contract for the saved summary set\n\n### Org summaries\n\n- `\u003cperiod\u003e/summary.json`: machine-readable org rollup for that period\n- `\u003cperiod\u003e/summary.md`: human-readable org summary for that period\n- `latest/summary.json` and `latest/summary.md`: convenience copies of the\n  newest period outputs\n- `index.json`: machine-readable map of the latest summary pair and all saved\n  history\n- `README.md`: human-readable history table\n- `contract.json`: run contract for the saved summary set\n\n## Latest and Index Files\n\nUse the `latest` files when downstream automation only needs the newest summary\nwithout first resolving a period key.\n\nUse the `index.json` files when automation needs:\n\n- the newest available period key\n- the source path behind the `latest` copy\n- the full saved history for the current contract\n- the manifest's latest run metadata and watermarks\n\nUse the generated `README.md` files when a human operator needs to inspect the\nsame catalog without opening JSON directly.\n\n## Current Metrics\n\n- Merged PR count\n- Time to merge\n- Time to first review\n- PR size\n- Commit count\n- Active author count\n- Merged PR per active author\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsjquant%2Forgpulse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsjquant%2Forgpulse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsjquant%2Forgpulse/lists"}