Files
granthi-review/README.md
T
2026-08-18 23:25:58 -04:00

98 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# granthi-review
Estate-wide AI code review for Granthi (Gitea) forges. One central
webhook-driven service — every repo gets reviews with **zero per-repo CI
setup**. Powered by shre-router.
## Architecture
```
Gitea (peer :3030) ──webhook (push, pull_request, HMAC-signed)──▶ granthi-review (:5498)
per-repo serialization queue, global LLM concurrency 1
diff fetch (PR .diff / per-commit .diff) + CLAUDE.md/CONTRIBUTING.md head
shre-router POST /v1/chat (agentId: code-reviewer)
strict-JSON rubric → 8 axes + findings + verdict
commit status 'granthi-review' + PR comment (scorecard) + sqlite ledger
GET /review/{owner}/{repo} (history dashboard)
nightly per-agent digests → ~/.granthi-review/digests/
```
Components (all in `src/`):
- `server.js` — HTTP server: `POST /webhook`, `GET /health`, `GET /review/:owner/:repo`; `--digest` CLI mode.
- `lib/webhook.js` — HMAC verification (`X-Gitea-Signature`, sha256 over raw body) + event → job mapping. Handles `push` and `pull_request` (opened / synchronize / reopened).
- `lib/queue.js` — per-repo serialization; global mutex around LLM calls (concurrency 1).
- `lib/review.js` — the pipeline. Diff capped at 60KB (truncation logged and disclosed to the model and in the comment).
- `lib/prompt.js` — rubric prompt requiring strict JSON: 8 axes (`correctness, security, tests_coverage, design_simplicity, performance, style, breaking_changes, docs`), each `{score 0-100, rationale}`, plus `overall`, `grade AF`, `findings[]`, `verdict`.
- `lib/jsonExtract.js` — tolerant JSON extractor (fences, prose, `<think>` blocks, trailing commas) + one retry on parse failure.
- `lib/verdict.js`**service-side verdict derivation is authoritative**: `fail` iff a finding has severity ∈ {critical, high} AND confidence = confirmed in axes correctness/security (untagged confirmed criticals also block, conservatively).
- `lib/trailers.js``Co-Authored-By` trailer parser for agent attribution.
- `lib/db.js` — sqlite ledger (`node:sqlite`, zero deps): repo, sha, pr, pusher, authors, trailers, axes, grade, findings, timings.
- `lib/digest.js` — nightly per-agent digests (score trend, recurring finding categories, last N reviews).
Dependencies: **none**. Node ≥ 22.13 (uses `node:sqlite`, global `fetch`).
## Review posture (approved)
**BLOCK on confirmed-critical, with human override.**
- Commit status context: `granthi-review`. verdict pass → `success`; warn → `success` with warning description; fail → `failure`.
- Branch protection on pilot repos requires the `granthi-review` context but does **not** apply to admins — an admin merge is the override. The blocking comment says so explicitly.
- **Fail-open with visibility**: if the router/LLM is down or output is unparseable after retry, the service posts status `warning` with "review unavailable" — it never blocks silently and never fakes a pass.
- Bare pushes (no PR): commit status + ledger + dashboard only. Gitea 1.27 has no commit-comment API endpoint, so no comment is posted for pushes.
## Config
`~/.granthi-review/config.json` (chmod 600):
```json
{
"port": 5498,
"forgeBaseUrl": "http://localhost:3030",
"botToken": "<shre-reviewer API token: write:repository,write:issue>",
"webhookSecret": "<shared HMAC secret>",
"routerUrl": "http://localhost:5497",
"model": "anthropic/claude-sonnet-4-6",
"tenantId": "nirlab",
"publicBaseUrl": "http://localhost:5498"
}
```
Bot identity: Gitea user `shre-reviewer` (created via `gitea admin user create`).
## Deploy (Mac dev tier)
- `launchd/ai.granthi.review.plist``~/Library/LaunchAgents/`, KeepAlive service on :5498.
- `launchd/ai.granthi.review.digest.plist` → nightly 03:30 `--digest` run.
- Webhooks: Gitea **system webhook** (admin → hooks) pointed at `http://host.docker.internal:5498/webhook` (the gitea container reaches the host that way), events push + pull_request, secret = `webhookSecret`. Fallback: per-repo hooks on pilot repos.
- Pilot gating: `nirpa/gitea-distro` main branch protection lists `granthi-review` in `status_check_contexts` (alongside the CI build context); `enable_approvals_whitelist`/admin-bypass left so admins can merge over a block.
## Adding the Reviews tab later (Granthi overlay)
The dashboard page `GET /review/{owner}/{repo}` is designed to be iframed or
linked as a repo tab. In the gitea-distro overlay, add an extra-tabs template
(`custom/templates/custom/extra_tabs.tmpl`):
```html
<a class="item" href="http://<review-host>:5498/review/{{.Repository.Owner.Name}}/{{.Repository.Name}}" target="_blank">
{{svg "octicon-checklist"}} Reviews
</a>
```
(For prod, front the service behind the granthi domain and use a relative
path instead of the host-port URL.)
## Tests
```
npm test # node --test: JSON extractor, verdict derivation, trailer parser
```
<!-- live-test marker 20260818 -->