- TypeScript 86.9%
- Svelte 8.3%
- JavaScript 2.9%
- Shell 1.5%
- Dockerfile 0.4%
Promise.race(publish, deadline) doesn't cancel the losing redis.publish, so a deadline-timeout means the publish was dispatched (and idempotent cancel/restart may have delivered) → 202 dispatched-unconfirmed, not a misleading 503. PublishDeadlineError distinguishes it from a real Redis rejection (still 503). Deadlines injectable. +CHANGELOG backfill for multi-origin OAuth (#11). full suite 2169. |
||
|---|---|---|
| .claude | ||
| .forgejo/workflows | ||
| assets | ||
| bin | ||
| deploy | ||
| docs | ||
| scripts | ||
| src | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| svelte.config.js | ||
| TODO.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
| tsconfig.refresher.json | ||
| tsconfig.svelte.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
hib-pr-reviewer
A standalone multi-round PR reviewer for Forgejo. Consumes a Redis-backed queue of PR events, runs the Claude Agent SDK against each diff (optionally as an adversarial quorum), and posts structured findings as Forgejo PR comments — read-only, no push/approve/merge/label rights ever.
Originally developed within the aim2be meta-repo (as a fresh-tree fork of ~/code/vioxen/quanta-pr-reviewer patterns — rule 53, never modifies the upstream); now standalone at ~/code/claude/other/hib-pr-reviewer with its Forgejo home at hib/hib-pr-reviewer. The aim2be NAS deployment that drove its development continues to work by configuring the same code via env vars.
Headline features
- Multi-round semantics. Round 1 on PR open, Round N on every push. Per-PR state in Redis (carried findings, comment IDs, round counter). Stale round parent comments collapsed into
<details>blocks — never deleted. - Adversarial quorum (opt-in). Two independent reviewers + one arbiter, each in its own Claude Agent SDK session. Arbiter reconciles, drops unverifiable claims, keeps cross-reviewer agreed findings.
- Structured bookkeeping (rule 68). Every parent comment carries header + TL;DR + summary + findings table + verdict + provenance footer. Future sessions reconstruct PR state in O(1) skim.
- Blast-radius context. When fed code-intelligence artefacts (
code-intelligence/<sub>/{repo-map,hierarchy,.api-surface}.json), the reviewer's prompt is enriched with downstream-impact paragraphs + sensitive-function flags + numeric blast-radius score. - Lazy-wait fresh intel. Auto-enqueues an
intel-refreshjob when blast-radius detects stale code-intelligence vs the PR base-branch HEAD, then waits for the refresh to complete before reviewing. - Memora-backed memory across rounds + PRs. Optional integration with a Memora MCP server. Per-PR + per-repo + per-finding memory; reviewer prompts include recall instructions.
- Forgejo webhook receiver. HMAC-SHA256 verified, allow-list filtered, dedupe-aware. Forwards events to the bee-queue.
- Operator status page with live SSE. Recent jobs grouped by PR, 4-tier severity badges (B/M/m/i + colour-only + two-line totals/latest), tri-state PR-link colors (teal merged / violet pending / grey closed-no-merge), outcome icons, settings cog + modal overlay, allow-list editor with dynamic org/repo dropdowns + bulk-add.
- Force actions.
/cancel-all,/recheck-webhook/:jobId,/reset-total,/flush-pressure,/intel-refresh. All structured-logged. - Bot identity (dual-token). Operator PAT for reads (PR info, diff, clone). Dedicated bot PAT for writes (post comment, patch prior round, file issue). Falls back to single-token mode when bot token unset.
- Cancel endpoint.
POST /cancel/:jobId→ Redis pubsub → SDK abort. In-flight jobs interrupted cleanly. - Auto-issue filing. Findings marked
defer: truebecome Forgejo issues with idempotent dedup, recurrence comments on re-surface. - Prometheus
/metrics+ JSON/health+ structured/version. - Single-image, dual-binary.
src/worker.ts(reviewer) +bin/intel-refresher.ts(intel sidecar) ship from the same image.
Quick start
Local dev
npm install
ALLOWED_REPO_OWNERS=my-org \
FORGEJO_TOKEN=<a-PAT-with-PR-comment-scope> \
FORGEJO_BASE_URL=https://git.example.com \
REDIS_URL=redis://localhost:6379 \
npm run dev
Then enqueue a job:
node -e '
const BeeQueue = require("bee-queue");
const q = new BeeQueue("pr-review", { redis: { host: "localhost", port: 6379 } });
q.createJob({
repo_owner: "my-org",
repo_name: "my-repo",
pr_number: 42,
pr_head_sha: "abcdef1234567",
pr_base_branch: "main",
pr_title: "feat: x",
trigger: "opened",
enqueued_at: new Date().toISOString(),
}).save().then(() => process.exit());
'
NAS deploy via docker compose
See docs/deployment.md for the full walkthrough. Short version:
# From your laptop, against your NAS reachable as SSH alias `NAS`:
ALLOWED_REPO_OWNERS=my-org \
NAS_DEPLOY_DIR=/home/hibryda/hib-pr-reviewer \
MEMORA_BASE_IMAGE=local/memora-server:0.2.28 \
bash deploy/install-on-nas.sh
The script builds the image locally, streams it to the NAS, materialises the secrets env file from ~/.config/claude/llm-keys.env, brings up the compose stack (6 containers: engine, viewport, svelte-viewport, redis, memora, intel-refresher), and verifies /health.
systemd-user (laptop dev)
cp deploy/systemd/hib-pr-reviewer.service ~/.config/systemd/user/
cp deploy/systemd/hib-pr-reviewer-redis.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now hib-pr-reviewer.service
Documentation
| Doc | Covers |
|---|---|
docs/architecture.md |
System architecture: 4 conceptual modules (Governor / Supervisor / Reviewer / Frontend / Shared) + the bee-queue + Redis message flow. |
docs/features.md |
Comprehensive feature inventory. For each feature: what + where in code + env vars + status-page surface. |
docs/deployment.md |
NAS deploy walkthrough. Container topology, env file, Forgejo webhook setup, bulk-installer for 18 repos. |
docs/configuration.md |
Every env var: name + default + accepted values + description. |
docs/multi-tenancy.md |
Allow-list mechanics, /bot-allowlist endpoint, Redis pub/sub propagation, dynamic dropdowns + bulk-add. |
docs/bot-identity.md |
Dual-token operator + bot model. Forgejo user creation, org permission scoping, token rotation. |
docs/status-page.md |
Operator status page on :9723. Recent-jobs grouping, sev-badges, PR-link colors, outcome icons, settings overlay. |
docs/multi-round.md |
R-cycle semantics, stale-comment collapse, structured bookkeeping, blast-radius, lazy-wait, prGroupKey. |
docs/cancel-endpoint.md |
POST /cancel/:jobId semantics — what cancels and what doesn't. |
docs/operations.md |
Operator workflows: rotate tokens, retry a job, restart a container, bulk-rewire webhooks. |
Behavioural rules (rule 58)
- Read-only on PR source. No push, approve, merge, label, close.
- Allow-listed targets. Owner + prefix filter applied at boundary (job validation + webhook receiver). Anything else dropped silently.
- Multi-round. Round 1 on PR open, Round N on each push.
- One parent comment per round. Stale findings from earlier rounds collapsed, not deleted.
- Rate-limit respect. Token pool rotates PATs to spread load.
- No findings spam. Empty rounds say "No new findings."
- Findings must be actionable. file:line + concrete change or question.
- Blast-radius context. Reviewer prompt includes
/quanta:blast-radiusoutput when wired. - Failure quiet. Crashed worker logs the error; never posts a half-baked partial comment.
Tests
npm test # vitest, no network
npm run typecheck # tsc --noEmit
1145+ tests cover: prompt builders, diff parser, allow-list, queue plumbing, webhook event mapper, intel freshness, blast-radius loader, comment renderer (rule 68 bookkeeping), collapse marker, issue-filer dedup, status server, SSE plumbing, force actions, irregularity detector, session supervisor, phase ledger, semaphore, repo checkout, retry classifier, pressure resolver, quorum + arbiter happy path, settings overlay propagation.
See also
- Upstream pattern reference (read-only):
~/code/vioxen/quanta-pr-reviewer/{README.md, CLAUDE.md}. - Behavioural rules carried over from the originating aim2be meta-repo, now part of this repo's own operating contract (see
CLAUDE.md+./.claude/rules/):- Rule 53 (borrowed reference repos — read-only treatment of
~/code/vioxen/quanta-pr-reviewer). - Rule 58 (PR reviewer discipline — read-only, allow-listed, multi-round; the verbatim list lives in
CLAUDE.md). - Rule 68 (structured bookkeeping — header + TL;DR + findings table + verdict + footer on every round comment).
- Rule 53 (borrowed reference repos — read-only treatment of