Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .codex/verify.commands
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# codex-os-managed
uv run python -m src.cli report saagpatel --portfolio-truth
PYTHONPATH=src uv run python -m github_repo_auditor.cli report saagpatel --portfolio-truth
uv run --extra dev ruff check .
uv run --extra dev --extra serve --extra semantic --extra config pytest -q
PYTHONPATH=src uv run --extra dev --extra serve --extra semantic --extra config pytest -q
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ NOTION_TOKEN=
# AUDIT_OUTPUT_DIR=output

# Optional: keep this around if you frequently use the flagship workbook path.
# The committed default remains assets/excel/analyst-template.xlsx.
# The committed default remains src/github_repo_auditor/assets/excel/analyst-template.xlsx.
# AUDIT_EXCEL_MODE=template
2 changes: 1 addition & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Steps:
2. Restore cached audit history and incremental fingerprints.
3. Run the audit in `standard` workbook mode, using incremental mode when a trustworthy cached baseline already exists.
4. Run `audit <username> --control-center` to generate the read-only operator triage artifact.
5. Inspect the canonical scheduled handoff issue state, then run `python3 -m src.scheduled_handoff --output-dir output ...` to build the scheduled handoff JSON + Markdown summary with the right lifecycle action.
5. Inspect the canonical scheduled handoff issue state, then run `python3 -m github_repo_auditor.scheduled_handoff --output-dir output ...` to build the scheduled handoff JSON + Markdown summary with the right lifecycle action.
6. Upload `output/` as the primary artifact output.
7. Open, update, close, or reopen one canonical `scheduled-audit-handoff` issue depending on whether the latest handoff is noisy or quiet.

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:

- name: Build scheduled handoff artifact
run: |
python3 -m src.scheduled_handoff \
python3 -m github_repo_auditor.scheduled_handoff \
--output-dir output \
--issue-state "${{ steps.issue-state.outputs.issue_state }}" \
--issue-number "${{ steps.issue-state.outputs.issue_number }}" \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
- name: Lint with ruff
run: ruff check src/ tests/
- name: Type-check operator trend seams
run: python -m mypy src/operator_resolution_trend.py src/operator_trend_support.py src/operator_trend_pending_text.py src/operator_trend_pending_debt.py src/operator_trend_transition_closure.py src/operator_trend_decision_memory.py src/operator_trend_recommendation_drift.py src/operator_trend_confidence_calibration.py src/operator_trend_trust_policy_history.py src/operator_trend_exception_recovery.py src/operator_trend_class_transitions.py src/operator_trend_run_context.py src/operator_trend_summary_context.py src/operator_trend_apply_chain.py src/operator_trend_topline_payload.py
run: python -m mypy src/github_repo_auditor/operator_resolution_trend.py src/github_repo_auditor/operator_trend_support.py src/github_repo_auditor/operator_trend_pending_text.py src/github_repo_auditor/operator_trend_pending_debt.py src/github_repo_auditor/operator_trend_transition_closure.py src/github_repo_auditor/operator_trend_decision_memory.py src/github_repo_auditor/operator_trend_recommendation_drift.py src/github_repo_auditor/operator_trend_confidence_calibration.py src/github_repo_auditor/operator_trend_trust_policy_history.py src/github_repo_auditor/operator_trend_exception_recovery.py src/github_repo_auditor/operator_trend_class_transitions.py src/github_repo_auditor/operator_trend_run_context.py src/github_repo_auditor/operator_trend_summary_context.py src/github_repo_auditor/operator_trend_apply_chain.py src/github_repo_auditor/operator_trend_topline_payload.py
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The current machine-readable truth surface is `output/portfolio-truth-latest.jso
Refresh and verify the local portfolio truth snapshot:

```sh
uv run python -m src.cli report saagpatel --portfolio-truth
uv run python -m github_repo_auditor.cli report saagpatel --portfolio-truth
jq '{generated_at,total:(.projects|length),counts:.source_summary.attention_state_counts}' output/portfolio-truth-latest.json
uv run operator-os-seam-linter --truth output/portfolio-truth-latest.json --json
```
Expand Down
42 changes: 21 additions & 21 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- GitHub Models as alternate narrative provider (`--narrative-provider {anthropic,github-models}`, `--narrative-model`) (Arc F S1.2).
- Dependabot/CodeQL/Secret-scanning alerts surfaced in the risk overlay (`--ghas-alerts`) (Arc F S1.3).
- README staleness + release-shipped signals (`has_any_release`, `release_count`, `latest_release_age_days`) (Arc F S1.4).
- mutmut pre-release mutation-testing gate on `src/auto_apply.py` + `src/scorer.py` (~93% combined kill rate). Documented in `docs/release-gates.md` (Arc F S1.5).
- mutmut pre-release mutation-testing gate on `src/github_repo_auditor/auto_apply.py` + `src/github_repo_auditor/scorer.py` (~93% combined kill rate). Documented in `docs/release-gates.md` (Arc F S1.5).
- Async fetch layer (`--fetch-mode {sync,async}`, `--fetch-workers N`) via httpx + asyncio.Semaphore. 9.7x speedup on the mock benchmark (Arc F S2.1).
- Per-(repo, commit-sha, analyzer) cache backed by SQLite with `--no-analyzer-cache` and `--reconcile-cache` controls (Arc F S2.2).
- SBOM fetch via GitHub API (`--sbom-source github`) + OSSF Scorecard (`--ossf-scorecard`) (Arc F S2.3).
Expand Down Expand Up @@ -148,23 +148,23 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [0.14.0] - 2026-03-29
### Added
- Security surface analyzer (`src/analyzers/security.py`): detects hardcoded secrets, exposed env files, overly-permissive configs
- README improvement suggestions engine (`src/readme_suggestions.py`): per-repo actionable diff
- Security surface analyzer (`src/github_repo_auditor/analyzers/security.py`): detects hardcoded secrets, exposed env files, overly-permissive configs
- README improvement suggestions engine (`src/github_repo_auditor/readme_suggestions.py`): per-repo actionable diff
- `--readme-suggest` flag to emit inline suggestions in Markdown report

## [0.13.0] - 2026-03-29
### Added
- Notion external signal integration: pulls star counts, watchers, and topic metadata from Notion
- Portfolio README generator (`src/portfolio_readme.py`): auto-generates a GitHub profile README from audit data
- Portfolio README generator (`src/github_repo_auditor/portfolio_readme.py`): auto-generates a GitHub profile README from audit data

## [0.12.0] - 2026-03-28
### Added
- Rich terminal output with color-coded tier badges, progress bars, and summary panels
- Unicode sparklines for commit activity trends in CLI output (`src/sparkline.py`)
- Unicode sparklines for commit activity trends in CLI output (`src/github_repo_auditor/sparkline.py`)

## [0.11.0] - 2026-03-28
### Added
- Shields.io badge generation per repo (`src/badge_export.py`)
- Shields.io badge generation per repo (`src/github_repo_auditor/badge_export.py`)
- Optional Gist upload for badge URLs via `--badges-gist` flag

## [0.10.0] - 2026-03-28
Expand All @@ -186,45 +186,45 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [0.7.0] - 2026-03-25
### Added
- Flagship Excel dashboard with 10 sheets and a full design system (`src/excel_export.py`, `src/excel_styles.py`)
- Flagship Excel dashboard with 10 sheets and a full design system (`src/github_repo_auditor/excel_export.py`, `src/github_repo_auditor/excel_styles.py`)
- Sheets: Summary, All Repos, Tier Breakdown, Top/Bottom 10, Language Distribution, Activity Heatmap, Dimension Radar

## [0.6.0] - 2026-03-25
### Added
- GraphQL client (`src/graphql_client.py`) for bulk repo queries, reducing API call count
- Library-years (libyears) staleness metric via `src/libyears.py`
- GraphQL client (`src/github_repo_auditor/graphql_client.py`) for bulk repo queries, reducing API call count
- Library-years (libyears) staleness metric via `src/github_repo_auditor/libyears.py`
- GitHub Releases detection in activity analyzer
- Radon cyclomatic complexity scoring in code quality analyzer
- Technology stack summary in report
- Trend tracking: week-over-week score deltas

## [0.5.0] - 2026-03-25
### Added
- Letter grades A–F for individual repos and portfolio health (`src/scorer.py`)
- Badge system: earned badges per repo based on dimension scores (`src/badges.py`)
- Quick wins: lowest-effort improvements highlighted per repo (`src/quick_wins.py`)
- Letter grades A–F for individual repos and portfolio health (`src/github_repo_auditor/scorer.py`)
- Badge system: earned badges per repo based on dimension scores (`src/github_repo_auditor/badges.py`)
- Quick wins: lowest-effort improvements highlighted per repo (`src/github_repo_auditor/quick_wins.py`)
- Commit pattern analysis: message quality, burst detection, solo vs. team commits
- Bus factor estimation from contributor stats

## [0.4.0] - 2026-03-25
### Added
- Dual-axis scoring: independent completeness and interest scores per repo
- Auto-sync: background re-audit of stale repos on a configurable schedule
- Audit history persistence across runs (`src/history.py`)
- Audit history persistence across runs (`src/github_repo_auditor/history.py`)
- Test suite expanded to 82 tests

## [0.3.1] - 2026-03-24
### Added
- Early Excel dashboard with 6 sheets and 6 charts
- Historical diff view: score changes between two audit runs (`src/diff.py`)
- Historical diff view: score changes between two audit runs (`src/github_repo_auditor/diff.py`)
- Swift/iOS-specific analyzer tuning
- CI pipeline for the auditor itself (GitHub Actions)

## [0.3.0] - 2026-03-24
### Added
- Registry reconciliation: cross-reference GitHub repos against a local `project-registry.md`
- `--registry` flag to specify the registry path
- GitHub API response cache (`src/cache.py`) with 1-hour TTL stored in `output/.cache/`
- GitHub API response cache (`src/github_repo_auditor/cache.py`) with 1-hour TTL stored in `output/.cache/`
- Summary statistics: most active, most neglected, highest/lowest scored, language distribution

## [0.2.0] - 2026-03-24
Expand All @@ -238,11 +238,11 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [0.1.0] - 2026-03-24
### Added
- Initial release: GitHub API client with pagination and rate-limit handling (`src/github_client.py`)
- Shallow clone pipeline via subprocess (`src/cloner.py`)
- `argparse` CLI entry point (`src/cli.py`)
- `RepoMetadata`, `AnalyzerResult`, `RepoAudit`, `AuditReport` dataclasses (`src/models.py`)
- Initial release: GitHub API client with pagination and rate-limit handling (`src/github_repo_auditor/github_client.py`)
- Shallow clone pipeline via subprocess (`src/github_repo_auditor/cloner.py`)
- `argparse` CLI entry point (`src/github_repo_auditor/cli.py`)
- `RepoMetadata`, `AnalyzerResult`, `RepoAudit`, `AuditReport` dataclasses (`src/github_repo_auditor/models.py`)
- 9 completeness analyzers: `readme`, `structure`, `code_quality`, `testing`, `cicd`, `dependencies`, `activity`, `documentation`, `build_readiness`
- `InterestAnalyzer` for tech novelty and project ambition scoring
- Weighted composite scorer with completeness tier classification (`src/scorer.py`)
- JSON and Markdown report output (`src/reporter.py`)
- Weighted composite scorer with completeness tier classification (`src/github_repo_auditor/scorer.py`)
- JSON and Markdown report output (`src/github_repo_auditor/reporter.py`)
16 changes: 8 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,23 @@ These conventions come from the project's `CLAUDE.md` and must be followed in al

## Adding a New Analyzer

Analyzers live in `src/analyzers/`. Each one scores a single dimension (0.0–1.0) and returns an `AnalyzerResult`.
Analyzers live in `src/github_repo_auditor/analyzers/`. Each one scores a single dimension (0.0–1.0) and returns an `AnalyzerResult`.

### Step 1 — Create the module

Create `src/analyzers/<your_dimension>.py` and implement a class that extends `BaseAnalyzer`:
Create `src/github_repo_auditor/analyzers/<your_dimension>.py` and implement a class that extends `BaseAnalyzer`:

```python
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

from src.analyzers.base import BaseAnalyzer
from src.models import AnalyzerResult, RepoMetadata
from github_repo_auditor.analyzers.base import BaseAnalyzer
from github_repo_auditor.models import AnalyzerResult, RepoMetadata

if TYPE_CHECKING:
from src.github_client import GitHubClient
from github_repo_auditor.github_client import GitHubClient


class YourDimensionAnalyzer(BaseAnalyzer):
Expand All @@ -128,13 +128,13 @@ class YourDimensionAnalyzer(BaseAnalyzer):

### Step 2 — Register the analyzer

Open `src/analyzers/__init__.py` and:
Open `src/github_repo_auditor/analyzers/__init__.py` and:

1. Import your new class at the top.
2. Append an instance to `ALL_ANALYZERS`.

```python
from src.analyzers.your_dimension import YourDimensionAnalyzer
from github_repo_auditor.analyzers.your_dimension import YourDimensionAnalyzer

ALL_ANALYZERS = [
...
Expand All @@ -144,7 +144,7 @@ ALL_ANALYZERS = [

### Step 3 — Add a weight in the scorer

Open `src/scorer.py` and add your dimension name to the `WEIGHTS` dict. Weights must sum to `1.0` after adding the new entry, so adjust existing weights proportionally.
Open `src/github_repo_auditor/scorer.py` and add your dimension name to the `WEIGHTS` dict. Weights must sum to `1.0` after adding the new entry, so adjust existing weights proportionally.

### Step 4 — Write tests

Expand Down
2 changes: 1 addition & 1 deletion DEPLOY.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,5 @@ image. Any Redis server version works (the throttle uses plain `EXPIRE`).
`SqliteWaitlistStore` needs a sibling implementation behind the existing
`WaitlistStore` protocol.
- **Local parity:** run the API with
`uv run --extra serve python -m uvicorn --factory src.serve.app:create_app --port 8080`
`uv run --extra serve python -m uvicorn --factory github_repo_auditor.serve.app:create_app --port 8080`
and the frontend with `pnpm dev` in `web/`.
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ COPY --from=ghcr.io/astral-sh/uv:0.5 /uv /bin/uv
WORKDIR /app
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
PYTHONPATH=/app/src \
PYTHONUNBUFFERED=1

# Install dependencies only (the app runs from the source tree via `src.*`
# Install dependencies only (the app runs from the source tree via `github_repo_auditor.*`
# imports, so the project itself isn't packaged). Cached unless deps change.
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project --no-dev --extra serve --extra hosting
Expand All @@ -22,5 +23,5 @@ EXPOSE 8080
# known proxy can opt in with GHRA_TRUST_FORWARDED_FOR and a platform-specific
# Uvicorn forwarded-allow-ips override.
CMD ["uv", "run", "--no-sync", "python", "-m", "uvicorn", \
"--factory", "src.serve.app:create_app", \
"--factory", "github_repo_auditor.serve.app:create_app", \
"--host", "0.0.0.0", "--port", "8080"]
28 changes: 8 additions & 20 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
.PHONY: install install-dev doctor audit control-center demo benchmark workbook-gate workbook-signoff test lint format type-check run clean release-gate build shiv dist-check release publish-pypi

PYTHON := python3
CLI := uv run python -m src.cli
SOURCE_ENV := PYTHONPATH=src
CLI := $(SOURCE_ENV) uv run python -m github_repo_auditor.cli
USERNAME ?= saagpatel
ARGS ?=

Expand All @@ -27,13 +28,13 @@ benchmark:
$(PYTHON) scripts/benchmark_large_portfolio.py

workbook-gate:
$(PYTHON) -m src.workbook_gate $(ARGS)
$(SOURCE_ENV) $(PYTHON) -m github_repo_auditor.workbook_gate $(ARGS)

workbook-signoff:
$(PYTHON) -m src.workbook_gate --record-signoff $(ARGS)
$(SOURCE_ENV) $(PYTHON) -m github_repo_auditor.workbook_gate --record-signoff $(ARGS)

test:
$(PYTHON) -m pytest tests/ -v
$(SOURCE_ENV) $(PYTHON) -m pytest tests/ -v

lint:
ruff check src/ tests/
Expand All @@ -49,24 +50,11 @@ run:

release-gate:
@echo "=== Running release gate: mutation testing ==="
@echo "Requires: python3.13, mutmut 2.x installed in python3.13 environment"
rm -rf .mutmut-cache mutants/
python3.13 -m mutmut run
@echo "Requires: Python 3.13 and the locked dev environment"
$(SOURCE_ENV) uv run --extra dev --python 3.13 mutmut run
@echo ""
@echo "=== Mutation results ==="
python3.13 -c "\
import sqlite3; \
conn = sqlite3.connect('.mutmut-cache'); \
rows = conn.execute('SELECT status, count(*) FROM Mutant GROUP BY status').fetchall(); \
total = sum(r[1] for r in rows); \
killed = next((r[1] for r in rows if r[0] == 'ok_killed'), 0); \
survived = next((r[1] for r in rows if r[0] == 'bad_survived'), 0); \
timeout = next((r[1] for r in rows if r[0] == 'bad_timeout'), 0); \
denom = killed + survived; \
rate = killed / denom if denom > 0 else 0.0; \
print(f'Total: {total} | Killed: {killed} | Survived: {survived} | Timeout: {timeout}'); \
print(f'Kill rate: {rate:.1%}'); \
exit(0 if rate >= 0.85 else 1)"
uv run --no-sync python scripts/check_mutation_score.py --minimum 0.85

clean:
rm -rf .pytest_cache __pycache__ dist build *.egg-info src/*.egg-info
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ The portfolio truth layer now has its own dedicated generation path. `--portfoli
After regenerating portfolio truth, verify the canonical snapshot instead of copying numbers into handoff or demo docs:

```bash
uv run python -m src.cli report saagpatel --portfolio-truth
PYTHONPATH=src uv run python -m github_repo_auditor.cli report saagpatel --portfolio-truth
jq '{generated_at,total:(.projects|length),counts:.source_summary.attention_state_counts}' output/portfolio-truth-latest.json
uv run operator-os-seam-linter --truth output/portfolio-truth-latest.json --json
```
Expand Down Expand Up @@ -410,7 +410,7 @@ Watch mode now uses that same baseline contract in live execution. Each cycle re
The workbook now supports two modes:

- `--excel-mode standard` — stable operational workbook path, the CLI default, and the recommended mode for automation and Mac Excel compatibility
- `--excel-mode template` — template-backed workbook path using `assets/excel/analyst-template.xlsx` for controlled template work
- `--excel-mode template` — template-backed workbook path using `src/github_repo_auditor/assets/excel/analyst-template.xlsx` for controlled template work

Both modes read from the same report + warehouse facts. Python owns the hidden `Data_*` sheets, stable table names, and workbook facts. The template-backed workbook still owns the template shell, named-range bindings, native sparkline placement, and print layout, but the standard workbook path is now the safest default for automated generation and Excel compatibility.

Expand Down Expand Up @@ -488,7 +488,7 @@ Common fixes:
- Missing GitHub token: set `GITHUB_TOKEN` or pass `--token` for private-repo access, GitHub writeback, metadata apply flows, and other authenticated actions.
- Missing or broken Notion config: create or fix `config/notion-config.json` before using `--notion-sync`, `--notion-registry`, or Notion writeback.
- Starting from scratch: copy `config/examples/audit-config.example.yaml` to `audit-config.yaml` and `config/examples/notion-config.example.json` to `config/notion-config.json`.
- Missing Excel template: restore `assets/excel/analyst-template.xlsx` or use `--excel-mode standard`.
- Missing Excel template: restore `src/github_repo_auditor/assets/excel/analyst-template.xlsx` or use `--excel-mode standard`.
- Missing baseline report: run a full audit before using `--repos`, `--incremental`, or other baseline-dependent workflows.
- Config/profile errors: fix `audit-config.yaml` syntax or choose an existing scoring profile under `config/scoring-profiles/`.

Expand Down
6 changes: 3 additions & 3 deletions config/custom-analyzers/dockerfile_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from pathlib import Path

from src.analyzers.base import BaseAnalyzer
from src.github_client import GitHubClient
from src.models import AnalyzerResult, RepoMetadata
from github_repo_auditor.analyzers.base import BaseAnalyzer
from github_repo_auditor.github_client import GitHubClient
from github_repo_auditor.models import AnalyzerResult, RepoMetadata


class DockerfileAnalyzer(BaseAnalyzer):
Expand Down
Loading
Loading