mergedhumanPR #12Other
[Platform] CI hygiene: Makefile, pre-commit, feedback bot, Definition of Done
Closes the gap where hackathon contributors run ruff check + pytest, call that "the tests", and ship PRs that fail CI on ruff format --check and/or pyright.
Description
The pitch.
## What this PR does
Closes the gap where hackathon contributors run `ruff check` + `pytest`, call that "the tests", and ship PRs that fail CI on `ruff format --check` and/or `pyright`. Five-file change, all additive — no participant code touched, no behavior change to the existing CI workflow.
### Files
1. **`Makefile`** (new)
- `make ci-local` runs the **exact** CI command sequence in order: `uv sync && uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run pytest -v`. One target, hard-fails on the first red command (Make's default behavior; no `set -e` gymnastics needed because each command is its own recipe line).
- `make hooks` installs pre-commit hooks via `uv run --with pre-commit pre-commit install` (no separate global install required).
- `make help` is the default goal — running bare `make` prints the menu.
2. **`.pre-commit-config.yaml`** (new)
- `astral-sh/ruff-pre-commit@v0.15.14`: `ruff` (with `--fix`) and `ruff-format`. Versions pinned to what `uv sync` resolves on `main` today.
- `RobertCraigie/pyright-python@v1.1.409`: invoked with `pass_filenames: false` so it runs the whole workspace (strict-mode type errors cross file boundaries; per-file is unreliable). Strict config picked up from `[tool.pyright]` in `pyproject.toml`.
- `default_stages: [pre-commit]` — hooks only fire on local commits. **Auto-fix happens locally**; CI never runs `pre-commit` (it runs the real `ruff`/`pyright`/`pytest` commands directly via `ci.yml`), so there is no path by which this config can mutate files in CI.
3. **`CONTRIBUTING.md`** — new "Definition of Done" section pinned to the very top, above all existing content. Lists the five exact commands every contributor MUST run before pushing in a single code block, with a one-line "why" for each (especially calling out the `ruff format --check` and `pyright` traps that bit 5/10 hackathon agents).
4. **`.github/workflows/ci-feedback.yml`** (new)
- Triggers on `workflow_run: completed` of the existing `CI` workflow, gated to `conclusion == 'failure'` AND `event == 'pull_request'`.
- Downloads the failing run's log zip via `gh api .../actions/runs/{id}/logs`, unzips, and runs an embedded Python extractor that produces ≤40-line excerpts per failing check (ruff format diff snippet, pyright error list, pytest failure summary).
- **Idempotent**: paginates `issues/{pr}/comments`, finds any comment containing the stable marker `<!-- ci-feedback-bot -->`, and PATCHes it in place if found; otherwise POSTs a new one. The bot never spams — at most one comment per PR, updated on every subsequent failure.
- Permissions scoped exactly as required: `pull-requests: write`, `actions: read`, `contents: read`. No write to contents, no checks API, no secrets beyond `GITHUB_TOKEN`.
- Comment body always ends with a link to `CONTRIBUTING.md#definition-of-done` plus the `make ci-local` one-liner.
5. **`README.md`** — small "Before you push" callout below the hello-world block linking to `CONTRIBUTING.md#definition-of-done`.
### Design decisions
- **Single Make target for the full sequence, not five sub-targets.** The whole point is that contributors today cherry-pick the steps that match their mental model of "tests". A single target removes that footgun. There is intentionally no `make lint` / `make test` shortcut that could be re-introduced.
- **Pre-commit pyright runs on the whole workspace, not staged files.** Strict-mode errors propagate across files; per-file checking gives false greens. Slightly slower commits, but matches what CI sees.
- **Feedback bot edits a single comment per PR keyed off an HTML marker, not `actor == 'github-actions[bot]'`.** The marker is stable across renames and survives someone hand-editing the body. It is also unambiguous in `body | contains(...)` jq filters.
- **The extractor is a single embedded Python heredoc, not a separate script file.** Keeps the workflow self-contained — no risk of a participant's PR bre
…Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/12/head:pr-12
git checkout pr-12