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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Claude PR Review

# Thin caller of the reusable in HarperFast/ai-review-prompts. The single
# `uses:` ref pin below controls everything that moves together — workflow
# logic, layer files, bash scripts, auth-gate behavior. Bumping the pin
# is the entire upgrade motion.
#
# Pre-requisites (org-level secrets, configured once on HarperFast):
# - HARPERFAST_AI_CLIENT_ID (the App's Client ID, like Iv23li…)
# - HARPERFAST_AI_APP_PRIVATE_KEY (.pem file contents)
#
# Plus the per-repo / inherited:
# - ANTHROPIC_API_KEY (required)
# - AI_REVIEW_LOG_TOKEN (optional — if set, threads each run
# into a per-PR issue in HarperFast/ai-review-log)

on:
pull_request:
# `labeled` admits the `claude-review` label gesture for
# bot-authored PRs (renovate, dependabot). See ai-review-prompts#38.
types: [opened, synchronize, reopened, ready_for_review, labeled]

# No concurrency block: cancellation is owned by the reusable's review
# job (job-level group, engaged only after authorization), so an
# unauthorized or skipped event can never cancel a legitimate review.

jobs:
review:
# Always-on toggle — see ai-review-prompts USAGE.md "Reviewers & the
# always-on toggle". CLAUDE_ALWAYS_ON=true (repo/org variable) → auto-
# review trusted-author PRs; unset → opt-in via the claude-review
# label. The reusable's authorize job still owns WHO is admitted.
# Note: the `claude-review` label name is deliberately duplicated —
# the caller gate names it (below) AND the reusable's authorize
# matches it; renaming the label means changing both.
# `ready_for_review` is admitted when the PR still carries the
# opt-in label, so a draft opted in via `claude-review` resumes
# review when it flips ready even with CLAUDE_ALWAYS_ON unset.
if: ${{ (github.event.action == 'labeled' && github.event.label.name == 'claude-review') || (github.event.action != 'labeled' && vars.CLAUDE_ALWAYS_ON == 'true') || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'claude-review')) }}
uses: HarperFast/ai-review-prompts/.github/workflows/_claude-review.yml@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission; #91 job-level cancellation; #90 cost gates; #89 defaults; #88 lenses)
# Caller-side permissions at the calling-job level (NOT workflow-
# level — that placement caps the reusable's per-job grants below
# what they need and breaks the workflow at startup; see
# ai-review-prompts#39/#40). Union of what the reusable's authorize
# (`contents: read`) and review (`contents: read` + `pull-requests:
# write` + `id-token: write`) jobs declare. Without this block the
# grants are inherited from the repo's default-workflow-permissions
# setting instead (GitHub intersects the reusable's requests with
# the caller's ceiling — un-grantable scopes are silently dropped,
# and `pull-requests: write` survives only while the repo default
# is "write"). Explicit grants keep the caller independent of that
# setting, and match gemini-review.yml in this repo.
permissions:
contents: read
pull-requests: write
id-token: write
with:
# Same SHA as the `uses:` ref above. The reusable uses this to
# check out HarperFast/ai-review-prompts (layer files + bash
# scripts) at the same ref as the workflow logic itself — keeps
# the upgrade motion atomic.
#
# The duplication is unavoidable: reusable workflows can't
# introspect their own ref (`github.workflow_ref` resolves to the
# CALLER's ref in `workflow_call` context), and `uses: …@<ref>`
# is parsed literally so we can't interpolate a variable.
ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde
review-layers: |
universal
repo-specific-checks: |
## Repo-specific checks (Harper documentation)

Docusaurus site, MDX + TypeScript config. The product here is PROSE that
readers act on, so a wrong sentence is a defect with the same standing as a
wrong line of code.

- **Doc-to-doc contradiction is the top measured defect class in this repo.**
When a page changes a documented default, option name, flag, endpoint or
version, the SIBLING pages that state the old value are now wrong. Check
whether the same fact is asserted elsewhere and left stale; that single class
accounted for 12 findings across 6 PRs in one post-mortem window.
- **Verify claims against Harper, not against the surrounding prose.** A code
sample, config key, CLI flag or default that no longer exists in the product
is a defect even when the paragraph reads well. Say so when you cannot verify
a claim from this repo alone.
- **Versioned docs**: `docusaurus docs:version` snapshots pages. A fix applied
only to the current version leaves the versioned copies wrong — flag which
copies a correction needs.
- **Redirects**: `historic-redirects.ts` keeps old URLs alive. Renaming or
moving a page without a redirect breaks external links.
- **Tooling**: `npm run typecheck` (tsc) and `format:check` (prettier).
`npm run lint` is a no-op stub (`echo 0`) — do not treat a green lint as
evidence of anything.
- Do not comment on formatting; prettier owns it.
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
AI_REVIEW_LOG_TOKEN: ${{ secrets.AI_REVIEW_LOG_TOKEN }}
HARPERFAST_AI_CLIENT_ID: ${{ secrets.HARPERFAST_AI_CLIENT_ID }}
HARPERFAST_AI_APP_PRIVATE_KEY: ${{ secrets.HARPERFAST_AI_APP_PRIVATE_KEY }}
104 changes: 104 additions & 0 deletions .github/workflows/gemini-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Gemini PR Review

# Thin caller of the Gemini reusable in HarperFast/ai-review-prompts.
# Runs in parallel with claude-review.yml so the two reviewers can be
# compared on the same PRs.
#
# Layer inputs and `repo-specific-checks:` MIRROR claude-review.yml in
# this repo. Output comparability between the two providers depends on
# them seeing the same review scope — keep them in sync when bumping
# the pin or editing the checks block.
#
# Opt-in by default: Gemini runs only when a HarperFast org member
# applies the `gemini-review` label, UNLESS the GEMINI_ALWAYS_ON repo
# variable is set to 'true' (then it auto-reviews trusted-author PRs,
# like claude-review). See ai-review-prompts USAGE.md "Reviewers & the
# always-on toggle".
#
# Pre-requisites:
# - HARPERFAST_AI_CLIENT_ID (org-level App Client ID)
# - HARPERFAST_AI_APP_PRIVATE_KEY (org-level App private key)
# - GEMINI_API_KEY (per-repo; optional — a missing key
# cleanly skips the review with a
# workflow notice, so this is safe to
# merge before the key is set)
# - AI_REVIEW_LOG_TOKEN (optional — threads each run into a
# per-(PR, provider) issue in
# HarperFast/ai-review-log with the
# `provider:gemini` label)

on:
pull_request:
# `labeled` admits the `gemini-review` opt-in gesture. `vars.*`
# can't be read in `on:` (only in a job `if:`), so the trigger
# lists the union and the `review` job gates on GEMINI_ALWAYS_ON.
types: [opened, synchronize, reopened, ready_for_review, labeled]

# No concurrency block: cancellation is owned by the reusable's review
# job (job-level group, engaged only after authorization), so an
# unauthorized or skipped event can never cancel a legitimate review.

jobs:
review:
# Always-on toggle — see ai-review-prompts USAGE.md "Reviewers & the
# always-on toggle". GEMINI_ALWAYS_ON=true (repo/org variable) → auto-
# review trusted-author PRs; unset → opt-in via the gemini-review
# label. The reusable's authorize job still owns WHO is admitted
# (CODEOWNERS trust set; the labeler, not the author, on `labeled`).
# Note: the `gemini-review` label name is deliberately duplicated —
# the caller gate names it (below) AND the reusable's authorize
# matches it; renaming the label means changing both.
# `ready_for_review` is admitted when the PR still carries the
# opt-in label — mirrors claude-review.yml.
if: ${{ (github.event.action == 'labeled' && github.event.label.name == 'gemini-review') || (github.event.action != 'labeled' && vars.GEMINI_ALWAYS_ON == 'true') || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'gemini-review')) }}
uses: HarperFast/ai-review-prompts/.github/workflows/_gemini-review.yml@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission; #91 job-level cancellation; #90 cost gates; #89 defaults; #88 lenses)
# Caller-side permissions at the calling-job level (NOT workflow-
# level — that placement caps the reusable's per-job grants below
# what they need and breaks the workflow at startup; see
# ai-review-prompts#39/#40). Union of what the reusable's authorize
# (`contents: read`) and review (`contents: read` + `pull-requests:
# write` + `id-token: write`) jobs declare.
permissions:
contents: read
pull-requests: write
id-token: write
with:
# Same SHA as the `uses:` ref above. The reusable uses this to
# check out HarperFast/ai-review-prompts (layer files + bash
# scripts) at the same ref as the workflow logic itself. The
# duplication is unavoidable: reusable workflows can't introspect
# their own ref (`github.workflow_ref` resolves to the CALLER's
# ref in workflow_call context), and `uses: …@<ref>` is literal.
ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde
review-layers: |
universal
repo-specific-checks: |
## Repo-specific checks (Harper documentation)

Docusaurus site, MDX + TypeScript config. The product here is PROSE that
readers act on, so a wrong sentence is a defect with the same standing as a
wrong line of code.

- **Doc-to-doc contradiction is the top measured defect class in this repo.**
When a page changes a documented default, option name, flag, endpoint or
version, the SIBLING pages that state the old value are now wrong. Check
whether the same fact is asserted elsewhere and left stale; that single class
accounted for 12 findings across 6 PRs in one post-mortem window.
- **Verify claims against Harper, not against the surrounding prose.** A code
sample, config key, CLI flag or default that no longer exists in the product
is a defect even when the paragraph reads well. Say so when you cannot verify
a claim from this repo alone.
- **Versioned docs**: `docusaurus docs:version` snapshots pages. A fix applied
only to the current version leaves the versioned copies wrong — flag which
copies a correction needs.
- **Redirects**: `historic-redirects.ts` keeps old URLs alive. Renaming or
moving a page without a redirect breaks external links.
- **Tooling**: `npm run typecheck` (tsc) and `format:check` (prettier).
`npm run lint` is a no-op stub (`echo 0`) — do not treat a green lint as
evidence of anything.
- Do not comment on formatting; prettier owns it.
secrets:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
AI_REVIEW_LOG_TOKEN: ${{ secrets.AI_REVIEW_LOG_TOKEN }}
HARPERFAST_AI_CLIENT_ID: ${{ secrets.HARPERFAST_AI_CLIENT_ID }}
HARPERFAST_AI_APP_PRIVATE_KEY: ${{ secrets.HARPERFAST_AI_APP_PRIVATE_KEY }}
35 changes: 35 additions & 0 deletions .github/workflows/validate-caller-workflows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Validate caller workflows

# Thin caller of HarperFast/ai-review-prompts'
# `_validate-caller-workflows.yml`. Validates
# `.github/workflows/claude-*.yml` caller files for:
#
# * Shadow jobs — a non-`uses:` job (or a `uses:` outside
# `HarperFast/`) alongside the legit reusable call would run
# with the caller's permissions WITHOUT going through the auth
# gate. Fail-closed.
# * Mutable refs in `uses:` or `with.ai-review-prompts-ref` —
# both must pin to a 40-char SHA.
#
# Runs on every PR and every push to `main` — no `paths:` filter.
# A required status check that only fires on workflow-touching PRs
# stays permanently pending on every other PR (GitHub has no
# "required if it runs" semantic). The validator's runtime is
# trivial (yq-parses a few caller files), so unconditional firing
# is the right trade-off for satisfiability.
#
# Make this `validate` job a REQUIRED status check on `main`.

on:
pull_request:
push:
branches: [main]

jobs:
validate:
uses: HarperFast/ai-review-prompts/.github/workflows/_validate-caller-workflows.yml@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission)
with:
# Same SHA as the `uses:` ref above — the reusable uses this
# to check out the validator script at the matching version.
# Same SHA-twice pattern as the other caller workflows.
ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde