ci: enforce PR policy (tests-present + app type-check) and PR template

All changes now land via PR. New .forgejo/workflows/pr-checks.yml runs on every PR
to master and (1) fails code PRs that lack a test change, (2) blocks new app-code type
errors. Unit tests are advisory until the baseline is green; lint is omitted (it needs
an interactive ESLint migration). PR template carries the docs/tests checklist.

Also makes the autofix watcher require a test (issue-12 style) + doc updates in every
fix, so its PRs satisfy the new gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hardik 2026-06-19 12:49:32 +05:30
parent 859be8c8d0
commit debac55a8a
4 changed files with 124 additions and 6 deletions

View file

@ -0,0 +1,17 @@
<!-- All changes land via PR — no direct pushes to master. -->
## What & why
<!-- Brief summary of the change and the motivation / linked issue (e.g. Closes #NN). -->
## Checklist
- [ ] **Tests** added or updated for this change — or it is a docs/config/automation-only PR (tests not applicable). Model: the integration test on `claude/issue-12` (prod-mirror DB, raw-SQL inserts, prefix-isolated, cleans up after itself).
- [ ] **Docs** updated where relevant (App/README.md, App/CLAUDE.md, Docs/, automation/README.md, CHANGELOG.md).
- [ ] `pnpm type-check` shows no new errors in application code.
- [ ] Verified the change (how: unit/integration tests, or a dev server on port 3100 against the test DB).
<!--
The "PR checks" workflow enforces the test-presence rule automatically:
a PR that touches App/app|lib|components|hooks without any test change will fail.
-->

View file

@ -0,0 +1,66 @@
name: PR checks
# Enforces the contribution policy on every PR into master:
# - code changes must ship with tests (docs/config/automation are exempt)
# - no new type errors in application code
# - unit tests are reported (advisory until the baseline is green)
# Runs on the pms1 host runner. See automation/README.md > "Contribution policy".
on:
pull_request:
branches: [master]
jobs:
checks:
runs-on: host
steps:
- name: Checkout PR
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Policy — code changes must include tests
run: |
set -uo pipefail
base="${GITHUB_BASE_REF:-master}"
git fetch origin "$base" --depth=200 -q
changed=$(git diff --name-only "origin/$base...HEAD")
printf 'Changed files:\n%s\n\n' "$changed"
# "Code" = app source (pages, API routes, lib, components, hooks).
# Tests, prisma, config, docs, automation and .forgejo are exempt.
code=$(printf '%s\n' "$changed" | grep -E '^App/(app|lib|components|hooks)/' \
| grep -vE '(\.test\.|\.spec\.|/tests/)' || true)
tests=$(printf '%s\n' "$changed" | grep -E '(\.test\.|\.spec\.|/tests/)' || true)
if [ -n "$code" ] && [ -z "$tests" ]; then
echo "::error::Code changed but no test files changed."
echo "Every code PR must add or update tests (model: the claude/issue-12 integration test)."
echo "If a test is genuinely not applicable, say why in the PR description so a reviewer can override."
printf '\nCode files without accompanying tests:\n%s\n' "$code"
exit 1
fi
echo "OK — test-presence policy satisfied."
- name: No new type errors in application code
run: |
set -uo pipefail
export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh"
cd App
pnpm install --frozen-lockfile
pnpm db:generate # prisma client types (no DB needed)
pnpm type-check > /tmp/tc.txt 2>&1 || true
# Gate on application-code errors only; the test suite has a known
# pre-existing type-mismatch baseline that is tracked separately.
app_errors=$(grep -E 'error TS' /tmp/tc.txt | grep -vE '/tests/|\.test\.|\.spec\.' || true)
if [ -n "$app_errors" ]; then
echo "::error::Type errors in application code:"; printf '%s\n' "$app_errors"
exit 1
fi
echo "OK — no type errors in application code."
- name: Unit tests (advisory)
continue-on-error: true
run: |
export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh"
cd App && pnpm test

View file

@ -32,6 +32,34 @@ Claude in a steered session). The triage breakdown comment is plain (no bot
marker) so, for `claude-queue` issues, the fix stage reads it back as refined
requirements.
## Contribution policy (all changes via PR)
**Every change lands through a pull request — no direct pushes to `master`.** This applies
to humans and to the automated pipeline alike (the watcher already opens PRs).
Each PR must include:
- **Tests** for any code change. Model: the integration test on `claude/issue-12`
it targets the prod-mirror test DB, anchors on existing rows, inserts fixtures via
raw SQL (schema-tolerant), isolates them with a unique prefix, and cleans up in
`afterEach`. Docs/config/automation-only PRs are exempt.
- **Docs** updates where relevant (`App/README.md`, `App/CLAUDE.md`, `Docs/`,
this file, `CHANGELOG.md`).
**Enforcement** — [`.forgejo/workflows/pr-checks.yml`](../.forgejo/workflows/pr-checks.yml)
runs on every PR into `master`:
1. **Test-presence gate (hard):** a PR touching `App/app|lib|components|hooks` with no
test change fails. Justify genuine exceptions in the PR body for a reviewer to override.
2. **App-code type-check (hard):** no new `tsc` errors in application code. (The test
suite has a known pre-existing type-mismatch baseline, tracked separately, so it is
filtered out of this gate.)
3. **Unit tests (advisory):** reported but non-blocking until the unit baseline is green
(2 known failures on `master`). `pnpm lint` is intentionally not run — it currently
requires an interactive ESLint migration.
A [`PULL_REQUEST_TEMPLATE.md`](../.forgejo/PULL_REQUEST_TEMPLATE.md) carries the checklist.
## Components
| Piece | Where | Notes |

View file

@ -282,13 +282,20 @@ while [ "$f" -lt "$n_fix" ]; do
printf '%s\n' " NEVER use a broad 'pkill -f next' -- it would kill the production app."
printf '%s\n' "- Never connect to or modify the production database or the production app."
printf '%s\n' ""
printf '%s\n' "## Your job"
printf '%s\n' "## Your job (PR policy: every code change ships with tests + docs)"
printf '%s\n' "1. Investigate the issue and implement a focused, minimal fix in this repository."
printf '%s\n' "2. Verify: run 'pnpm type-check' and 'pnpm lint' in App/. If behaviour is covered by unit"
printf '%s\n' " tests, run them; for DB-backed behaviour, run integration tests against the test DB above."
printf '%s\n' "3. Add or adjust tests when it makes sense."
printf '%s\n' "4. Commit ALL changes to the current branch with a conventional message ending: Fixes #$num"
printf '%s\n' "5. Do NOT push, do NOT create tags, do NOT switch branches. The supervisor handles push and PR."
printf '%s\n' "2. REQUIRED: add or update a test that fails before your fix and passes after. Model it on"
printf '%s\n' " App/tests/integration/dashboard-approved-this-month.test.ts (from issue #12): target the"
printf '%s\n' " prod-mirror test DB, anchor on existing rows (findFirstOrThrow), insert fixtures via raw"
printf '%s\n' " SQL with a unique prefix, and clean them up in afterEach. The PR check REJECTS code"
printf '%s\n' " changes under App/app|lib|components|hooks with no test change."
printf '%s\n' "3. Verify: 'pnpm type-check' (no new app-code errors) and run your test against the test DB:"
printf '%s\n' " cd App && set -a && . ./.env && set +a && pnpm test:integration"
printf '%s\n' "4. REQUIRED: update any docs the change affects (App/README.md, App/CLAUDE.md, Docs/,"
printf '%s\n' " CHANGELOG.md) — skip only if nothing documented is affected."
printf '%s\n' "5. Commit ALL changes (fix + test + docs) to the current branch with a conventional message"
printf '%s\n' " ending: Fixes #$num"
printf '%s\n' "6. Do NOT push, do NOT create tags, do NOT switch branches. The supervisor handles push and PR."
printf '%s\n' "If the issue is unclear, too risky (migrations, payments, permissions), or you cannot verify"
printf '%s\n' "the fix, make NO commits and write a short explanation to CLAUDE_RESULT.md in the repo root."
} > "$prompt_file"