{
  "markdown": "# rag-reviewer\n\n[Русский](README.ru.md)\n\nAI-assisted pull-request reviews grounded in whole-repository context: hybrid search, a code\ngraph, and inline comments anchored to changed lines.\n\n> Requires Python 3.11–3.13 and external Voyage, PostgreSQL/ParadeDB, and Neo4j services.\n> Publishing reviews also requires credentials for the selected version-control provider.\n\n[![PyPI](https://img.shields.io/pypi/v/rag-reviewer?color=2563eb&label=PyPI)](https://pypi.org/project/rag-reviewer/)\n[![Python 3.11–3.13](https://img.shields.io/badge/python-3.11%E2%80%933.13-2563eb)](https://pypi.org/project/rag-reviewer/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-22c55e)](LICENSE)\n\n## Start here\n\nChoose the shortest route for what you need now. Both routes lead to the same workflows and\nreference sections later in this document.\n\n| If you want to… | Follow |\n|---|---|\n| Try reviewer and get a first result | [Try reviewer](#try-reviewer) |\n| Use reviewer with a team on one shared host | [Deploy for a team](#deploy-for-a-team) |\n\n## Try reviewer\n\nYou need Python 3.11–3.13, [uv](https://docs.astral.sh/uv/), Docker, a Voyage API key, and a\nversion-control system (VCS) token if reviewer should read or publish pull-request reviews. The\nstores run locally; embedding and reranking requests go to Voyage.\n\n1. Install the launcher, synchronize reviewer's managed artifacts, start the stores, and configure\n   reviewer:\n\n   ```bash\n   uv tool install rag-reviewer\n   reviewer update\n   docker compose -f ~/.config/rag-reviewer/docker-compose.yml up -d\n   reviewer init\n   ```\n\n   `reviewer update` creates the managed Compose file next to the env file in\n   `$XDG_CONFIG_HOME/rag-reviewer/` (`~/.config/rag-reviewer/` by default). One store stack therefore\n   serves every repository, and the Compose project name stays independent of the current working\n   directory. The command also refreshes detected AI-client integrations and skills.\n\n2. See the supported AI clients and connect one:\n\n   ```bash\n   reviewer install --list\n   reviewer install codex\n   ```\n\n3. Build the branch-scoped searchable snapshot called the base index, then check the environment\n   and inspect index freshness:\n\n   ```bash\n   reviewer index /path/to/repo --ref main\n   reviewer check\n   reviewer status /path/to/repo --branch main --json\n   ```\n\n   Indexing initializes the `chunks` schema that `reviewer check` queries, so a fresh installation\n   must index before checking. The check validates every configured VCS provider; its successful\n   identity check does not prove repository-specific permissions. The status payload should show an\n   indexed SHA and `drift == 0`.\n   Full indexing sends code chunks to Voyage and can be slow on its free tier. Without a base\n   index, PR review has only the diff and its temporary changed-file index (overlay), and therefore\n   thinner repository context.\n\n4. Open a new client session and run the first review:\n\n   ```text\n   # Claude Code\n   /rag-reviewer:review-pr owner/repo#123 --dry-run\n\n   # Codex\n   $rag-reviewer:review-pr owner/repo#123\n   ```\n\n   Invocation syntax differs by client. A dry run returns grounded findings without publishing;\n   a normal run publishes through `publish_review` and therefore requires VCS write credentials.\n\nFor a temporary launcher without a persistent tool installation:\n\n```bash\nuvx --from rag-reviewer@latest reviewer\n```\n\n## Deploy for a team\n\nThis route assumes that team members open their AI-client sessions on one shared host under one\nservice account. Each client launches its own `reviewer-mcp` stdio process; those processes share\nPostgreSQL/ParadeDB and Neo4j through the Compose services bound to `127.0.0.1`, plus the service\naccount's reviewer env. It is not one central MCP daemon. MCP requests carry repository, branch,\nproject, and `provider_options`, and tool results return selected code context to the AI client.\nFor separate workstations, use secured network-accessible stores and configure their DSNs and\nreviewer env on every workstation instead of using the loopback Compose defaults.\n\n1. **On the shared host, start the stores and configure secrets for the service account.**\n\n   ```bash\n   reviewer update\n   docker compose -f ~/.config/rag-reviewer/docker-compose.yml up -d\n   reviewer init\n   ```\n\n2. **Choose repository and branch scope.** Set `DEFAULT_REPO` as the fallback repo, and either\n   the ordered `REVIEW_BRANCHES` CSV allowlist in server env or (preferred) a per-repo home\n   layer — see [Repositories and branches](#repositories-and-branches). Put repository-specific\n   policy, ignored paths, context limits, and non-secret board metadata in `.review.yml`.\n\n3. **Build and verify every tracked branch.**\n\n   ```bash\n   reviewer index /srv/rag_for_git --ref main --repo mimfort/rag_for_git\n   reviewer check\n   reviewer status /srv/rag_for_git --branch main --json\n   ```\n\n4. **Connect team clients.**\n\n   ```bash\n   reviewer install --all\n   reviewer install codex --dry-run\n   ```\n\n   Run installation on the shared host as the same service account. `--all` configures the\n   supported clients for that account; `--dry-run` reports planned config writes. Open a new chat\n   or CLI session afterwards; IDE integrations may also require Reload Window.\n\n5. **Add optional board context.** Select a registered provider in `.review.yml`, keep its\n   credentials in the reviewer env, and validate the exact project:\n\n   ```bash\n   reviewer check --board-project TYPE=PROJECT\n   ```\n\n   Repeat `--board-project` for additional providers. See [Task boards](#task-boards) and the\n   [provider reference](docs/board-providers.md).\n\n## Core workflows\n\nReviewer workflows are delivered as namespaced skills. Each skill defines its own read/write\nboundaries and confirmation gates; the MCP server performs storage, graph, VCS, and board work.\n\n### Review a pull request\n\nUse `review-pr` for bug finding. It prepares a PR session, retrieves code and graph\ncontext, analyzes changed files, verifies candidate findings, and publishes only grounded results.\nUse `--dry-run` first when validating a deployment. Inline comments can target only commentable\ndiff lines; off-diff findings go to the summary.\n\n### Solve a task\n\nUse `solve-task` to turn a board task or free-text request into a persisted brief before\ndevelopment. It checks index freshness, warms task context, gathers related work and code, then\nhands the brief to brainstorming. It does not implement the task by itself.\n\n### Ask a grounded codebase question\n\nUse `ask` for onboarding and codebase Q&A. Answers cite real `path:line` locations from\nthe base index and code graph. It reads and explains; it neither reviews a PR nor modifies code.\n\n### Walk a human reviewer through a PR\n\nUse `pr-walkthrough` for a reading guide: where to start, what each file changes, and\nwhich callers are affected. It is intentionally separate from bug review.\n\n### Run a focused review\n\nUse `performance-review` for repeated I/O, N+1 work, poor asymptotics, batching, caching,\nand memory risks. Use `maintainability-review` for complexity, duplication, readability,\nseparation of concerns, and repository conventions. Both stay within the requested dimension.\n\n### Create, decompose, and finish board tasks\n\n`create-task` drafts a canonical task body and writes only after confirmation.\n`decompose-task` turns one stored parent into a fully previewed native-child batch, asks for one\nconfirmation, preserves the previewed idempotency key on retries, then re-syncs and verifies every\nrelationship and child read.\n`finish-task` links the PR, moves the task to a discovered done target, adds a task link\nto the PR body, and re-syncs the task corpus—also only after confirmation.\n\n### Reviewer grounding in plan/review phases (optional)\n\n[Reviewer grounding in plan/review phases](#reviewer-grounding-in-planreview-phases-optional)\nlets planning and review phases reuse session-less reviewer tools when the base index is current.\n\n> **Reviewer grounding (plan/review, optional, fail-open).** Run\n> `reviewer status /path/to/repo --branch main --json` first. When `drift == 0`, prefer\n> `search_codebase` for cross-file facts and use `callers`, `related_symbols`, `definition`,\n> `implementations`, or `family` only for central symbols. The base index does not see\n> uncommitted edits, so read changed files from disk. If reviewer or the index is unavailable,\n> fall back to local search/read tools instead of blocking.\n\n- `family(repo, node_id, branch)` — the family of look-alike symbols (\"who else is\n  like this\"): inheritance plus structural contract match. For roll-out tasks\n  (\"add a field to every provider\"), where one file found is a representative of a\n  family of N.\n\n## How it works\n\nRAG means retrieval-augmented generation: the model receives code selected by hybrid semantic and\nlexical search instead of only the PR diff. The graph adds structural relationships.\n\n```text\nPR → prepare_review → base + overlay retrieval → skill analysis\n   → verify → policy gate → grounding → dedup → inline comments + summary → cleanup\n```\n\n- **Base index.** Persistent chunks live under `base:<branch>`. PostgreSQL/ParadeDB combines\n  pgvector approximate nearest-neighbor (ANN) search with BM25 lexical ranking; Voyage produces\n  embeddings and reranks candidates.\n- **Overlay.** Changed PR files use an ephemeral `pr:N` ref. Retrieval takes unchanged files from\n  base and changed files from overlay.\n- **Code graph.** Neo4j nodes use `node_id = path#fqn`, where `fqn` is the fully qualified name.\n  SCIP, an external type-aware code indexer, provides `CALLS` and method-level `IMPLEMENTS`; `auto`\n  falls back to tree-sitter `CALLS` plus class-level `IMPLEMENTS` (from syntax) when SCIP is\n  unavailable.\n- **Grounded publishing.** Findings must quote real changed code. GitHub suggestions are emitted\n  only when the replacement is safely applyable on the RIGHT side of the diff.\n- **Idempotency.** Hidden fingerprints prevent reposting the same finding. Overlay/session cleanup\n  runs after publication and fail-soft on errors.\n\nFor the module-level map and invariants, see [CLAUDE.md](CLAUDE.md).\n\n## Installation and configuration\n\n### Requirements\n\n- Python `>=3.11,<3.14`;\n- Docker for the default PostgreSQL/ParadeDB and Neo4j stack;\n- Voyage API credentials for embeddings and reranking;\n- VCS credentials for PR reads and publication;\n- a supported AI client with the reviewer MCP integration.\n\n### Installation and updates\n\nPersistent CLI:\n\n```bash\nuv tool install rag-reviewer\nreviewer update\n```\n\n`uv tool install` takes the package name and installs both of its commands, `reviewer` and\n`reviewer-mcp`. Its `--from` option only pins a different source for the same package\n(`--from rag-reviewer==0.4.3`, `--from git+…`); `--from PACKAGE COMMAND` is `uvx` syntax and\n`uv tool install` rejects it.\n\nFor the one-time transition from 0.4.3, start the new lifecycle through latest uvx and explicitly\nallow it to upgrade the existing persistent tool:\n\n```bash\nuvx --refresh --from rag-reviewer@latest reviewer update --upgrade-tool\n```\n\nEvery later update is the short command `reviewer update`. It performs one lifecycle:\n\n- checks PyPI and upgrades the persistent `uv tool` package when a newer version exists;\n- refreshes every detected AI-client MCP integration, native plugin, and file-based skill set;\n- synchronizes `$XDG_CONFIG_HOME/rag-reviewer/docker-compose.yml` from the canonical repository;\n- records the managed Compose content hash in `.reviewer-update.json`.\n\nIf the Compose file differs from its recorded hash, reviewer treats it as user-modified, leaves it\nunchanged, and prints a warning. Update does not run `docker compose pull`, restart services, remove\ncontainers, or delete volumes, so existing databases, indexes, tasks, and subsystem summaries stay\nintact. Apply a new Compose definition when convenient with the documented `docker compose ... up\n-d` command.\n\nTemporary/latest invocation:\n\n```bash\nuvx --from rag-reviewer@latest reviewer --help\n```\n\nAn ordinary uvx invocation never mutates a separate persistent tool; only the explicit\n`--upgrade-tool` bootstrap does. Use `reviewer install CLIENT --dry-run` to inspect a named\nintegration write.\n\n### AI clients\n\n`reviewer update` refreshes all detected clients automatically. Use `reviewer install --list` and a\nnamed install when connecting a client for the first time, before it can be detected:\n\n```bash\nreviewer install codex\nreviewer install --all\nreviewer install-skills codex\n```\n\nCodex-specific lifecycle:\n\n```bash\nuvx --from rag-reviewer@latest reviewer install codex\nuvx --from rag-reviewer@latest reviewer install codex --dry-run\ncodex plugin list --json\ncodex mcp list\n```\n\nClaude Code global plugin lifecycle:\n\n```bash\nuvx --from rag-reviewer@latest reviewer install claude-code\nclaude plugin list --json\nclaude plugin marketplace list --json\n```\n\nAfter installation or update, start a New Chat/new CLI session; in an IDE, also use Reload Window.\n\n### Breaking skill-name migration\n\nThis release removes the redundant `reviewer_` segment from every skill name. Legacy skill\ninvocations are unsupported: update the plugin/cache, use the short names listed below, then open\na New Chat or new CLI session. In an IDE, also use Reload Window.\n\n### Required services and credentials\n\nRun `reviewer init` to write the selected env file and `reviewer check` to validate it. Resolution\norder is `REVIEWER_ENV_FILE` → `$XDG_CONFIG_HOME/rag-reviewer/.env` → `./.env`.\n\nImportant groups:\n\n- Voyage: `VOYAGE_API_KEY`;\n- stores: `PG_DSN`, `NEO4J_URI`, `NEO4J_USER`, `NEO4J_PASSWORD`;\n- VCS: provider token plus optional API base;\n- repository scope: `DEFAULT_REPO`, `REVIEW_BRANCHES` (branch allowlist fallback; a per-repo home\n  layer takes precedence — see [Repositories and branches](#repositories-and-branches));\n- board credentials: provider-specific env declared in the registry.\n\nPublished host ports of the Compose storage services are variables, not literals:\n`PARADEDB_PUBLISH_PORT` (default `5433`), `NEO4J_BOLT_PUBLISH_PORT` (default `7687`) and\n`NEO4J_HTTP_PUBLISH_PORT` (default `7474`). Container ports stay fixed. `reviewer init` asks for\nthem in the storage group and derives the first two from `PG_DSN` and `NEO4J_URI`, so the client\nstring and the published port cannot drift apart silently; a mismatch on a local host prints a\nwarning without blocking.\n\n```bash\nPARADEDB_PUBLISH_PORT=6543 NEO4J_BOLT_PUBLISH_PORT=7999 \\\n  docker compose -f ~/.config/rag-reviewer/docker-compose.yml up -d\n```\n\n`reviewer start` and `reviewer stop` manage that Compose file for you:\n\n```bash\nreviewer start   # up -d --wait, waits for the ParadeDB and Neo4j healthchecks\nreviewer stop    # stops the containers; named volumes and the built index survive\n```\n\n`reviewer stop` also stops the web admin when it was started with `--profile web`: without an\nexplicit profile selection docker compose does not see it. It leaves the test services\n(`--profile test`) alone — those belong to the repository clone's own Compose project. Both\nstorages declare `stop_grace_period: 60s`: the default 10s are not enough for the Neo4j JVM to\nshut down cleanly, which left the store to be recovered on the next start.\n\nBoth run under the explicit Compose project `rag-reviewer`. A clone of this repository runs its\nown stack under the project name `rag_for_git` — the two publish the same host ports and keep\nseparate volumes, so do not run them at the same time. Contributors working inside the clone\nshould keep using `docker compose up -d` there.\n\n`reviewer stop` never removes volumes: it runs `docker compose stop`, which has no `-v` flag at\nall.\n\nOn Docker Engine older than 25.0, the `start_interval` healthcheck key is ignored, so the first\nNeo4j probe only happens after the plain `interval` (300s) — exactly the `--wait` timeout used by\n`reviewer start`. On such engines `reviewer start` can report a timeout failure even though the\nstack came up fine; upgrading Docker Engine removes the issue.\n\nPrefer variables over editing the Compose file: a hand-edited\n`~/.config/rag-reviewer/docker-compose.yml` no longer matches its recorded hash, so `reviewer\nupdate` treats it as user-modified (status `preserved`) and stops delivering new Compose\ndefinitions to it. A `preserved` Compose file also stops receiving new healthcheck definitions, so\n`reviewer start` falls back to waiting for the `running` state instead of real readiness.\n\nCredentials stay server-side. **Credentials are not returned** by board metadata or discovery\ntools and must not be placed in `.review.yml`.\n\n### Configuration ownership\n\n| Location | Owner | Stores | Must not store |\n|---|---|---|---|\n| global `.env` | deployment/operator | secrets, credentials, DSNs, runtime infrastructure and compatibility fallbacks | repository policy |\n| home global YAML | OS account running reviewer | shared non-secret defaults | credentials |\n| home per-repo YAML | OS account running reviewer | `repository.primary_branch`, `repository.index_branches`, operator-owned repo policy | credentials |\n| committed `.review.yml` | repository team | team-visible review policy and non-secret task-board metadata | credentials or `repository` |\n| git remote / CLI | repository/operator | canonical `owner/name` identity and explicit command overrides | persisted secrets |\n| Postgres / Neo4j | reviewer runtime | derived indexes, task/review state and code graph | source-of-truth configuration |\n\n#### Single repository\n\nRun `reviewer init` from the clone, inspect the global `.env` and home per-repo previews, then run\n`reviewer check` and `reviewer config show --repo owner/name`.\n\n#### Second repository\n\nRun `reviewer init --scope repo` from the second clone. It creates or previews only that repository's\nhome per-repo YAML and does not rewrite global `.env` or the first repository's config.\n\n#### CI / server\n\nInject secrets into global `.env` or the process from a secret manager. Use noninteractive init only\nfor deterministic preview/write, mount home YAML for the service account, and keep team-owned policy\nin committed `.review.yml`. Pass `--repo owner/name` when no usable git remote is present.\n\n### VCS credentials\n\n| Provider | Environment | Minimum access | Reviewer reads | Reviewer writes | `reviewer check` |\n|---|---|---|---|---|---|\n| GitHub | `GITHUB_TOKEN` | fine-grained PAT: Pull requests: Read and write; Contents: Read | PR metadata, files, comments, contents, compare | review comments/summary and PR body backlink | authenticates `/user` identity |\n| GitLab | `GITLAB_URL`, `GITLAB_TOKEN` | PAT/project token with `api` scope | MR metadata, changes, notes, repository files, compare | discussions/notes and MR description backlink | authenticates `/api/v4/user` identity |\n\nThe health check proves URL/token authentication, not every granular repository permission. The\nselected repository permissions are exercised by an actual review. `reviewer init` shows the same\ncontract before prompting only for the selected provider's credentials.\n\n### Repositories and branches\n\n`DEFAULT_REPO` identifies the fallback `owner/name`. The repo tag is resolved as `--repo` →\n`git remote origin` → `DEFAULT_REPO`, and the resolution reports its own origin: `cli`,\n`git:origin`, or `env:DEFAULT_REPO`. Because an index written under the wrong tag surfaces only\nas odd search results, `reviewer index` **refuses** to write when the name was substituted from\n`DEFAULT_REPO` rather than derived from the clone — pass `--repo owner/name` or fix the origin\nURL. `reviewer status` stays fail-open and instead exposes the origin: a warning line in the text\noutput and a `repo_source` key in `--json`.\n\nTracked branches for a repository are\nresolved in layered order — the first source that defines them wins entirely (no per-branch\nmerge): a per-repo home file `$XDG_CONFIG_HOME/rag-reviewer/repos/<owner>/<name>.yml` →\nthe home-global `review.yml` → the env `REVIEW_BRANCHES` CSV allowlist → `[\"main\"]`. In every\nsource the first entry is primary unless `primary_branch` is set explicitly. Each branch has\nisolated `base:<branch>` chunks and graph nodes. Run `reviewer config show --repo owner/name`\nto see the effective branches and which layer produced them.\n\n```bash\nreviewer index /path/to/repo --ref main --repo owner/name\nreviewer status /path/to/repo --branch main --json\nreviewer search \"token verification\" --branch main\n```\n\nUse `reviewer config migrate --repo owner/name` to copy the env `REVIEW_BRANCHES` allowlist into\nthe per-repo home layer (no-op if a home layer already sets branches), or `reviewer migrate-branches`\nonce when upgrading a legacy unscoped base index.\n\n### Per-repo `.review.yml`\n\nPer-repo policy overrides server defaults and is read from the target/base branch. Typical fields:\n\n```yaml\npaths:\n  ignore:\n    - generated\n\nsummary_cluster_depth: 2\nsummary_topk_threshold: 20\n\nsummary_paths:\n  ignore:\n    - tests\n    - test\n\ncontext_limits:\n  search_codebase:\n    floor: 4\n    ceiling: 15\n  graph:\n    hops: 1\n  code_section:\n    max_files: 20\n    max_chunks_per_file: 1\n    chars_per_file: 975\n    max_augmented_files: 3\n```\n\n`summary_paths.ignore` only filters which files feed subsystem-summary clustering — unlike\n`paths.ignore`, it does not affect indexing or PR review. Default is `[\"tests\", \"test\"]`; there\nis no env layer (like `context_limits`), and an explicit empty list disables the filter.\n\n`context_limits` has four subsections: `search_codebase` (hybrid + graph-expansion + Voyage\nrerank for `/ask`, priming, and PR review), `search_tasks` (RRF-only task retrieval), `graph`\n(traversal depth from top hits), and `code_section` — the file budget for the task context's\n`code` section (PRI-256, defaults widened in PRI-259). `code_section`'s budget unit is a file,\nnot a chunk: the section holds up to `max_files` files, each contributing up to\n`max_chunks_per_file` chunks of `chars_per_file` characters. The section's\ncharacter cap is not a separate key — it is derived: the operational budget is\n`max_files × max_chunks_per_file × chars_per_file`, while the post-render safety cap is\n`max_files × max_chunks_per_file × chars_per_file × 3 // 2`. The default trades width for depth\n(`12 × 1300` → `20 × 975`), growing the operational budget by 25 % (15,600 → 19,500) to raise\nbulk core-recall past the acceptance threshold; `chars_per_file` has a floor of 975 (enough to\nread a symbol's signature plus a few lines of body) because the recall metric only counts paths\nand is blind to fragment depth. See `eval/replay_report.md`, \"Приёмка PRI-259\".\n\n`code_section.max_augmented_files` (default 3, PRI-257) mixes actual diff paths from similar\ntasks into the `code` section — a single source (`similar-diffs`, from the `brief_quality`\ntable plus a git-log fallback keyed on the task ID). It is a *reserve* of file slots inside\n`max_files`, not a cap on what's left over: the hybrid retrieval fills its full `max_files`\nbudget first, and only against that final output — not the raw retrieval pool — is a candidate\npath judged \"already known\" (checking against the raw pool would discard exactly the files this\nlever exists to surface). With no augmented candidates, the hybrid keeps the entire budget. A\nco-change (git file-pairs-changed-together) second source was built and measured — 4 core hits on\n34 mixed-in paths, a bulk-recall drop — and removed rather than kept disabled; only similar-diffs\ncovers (median core-recall 0.5 → 0.75, precision 0.167 → 0.333, 28 hits on 35 paths). See\n`eval/replay_report.md`, \"Приёмка PRI-257\".\n\n### Layered repository policy\n\nPolicy is resolved in this exact order; each later source wins for the same leaf key:\n\n```text\nENV\n  < $XDG_CONFIG_HOME/rag-reviewer/review.yml\n  < committed .review.yml at the selected target ref\n  < $XDG_CONFIG_HOME/rag-reviewer/repos/<owner>/<name>.yml\n```\n\nWhen `XDG_CONFIG_HOME` is unset, the home root is `~/.config/rag-reviewer`. Merging is **recursive\nover mapping values**: a later layer that says nothing about a subsection does not erase it, so a\nhome `context_limits: {graph: {hops: 2}}` keeps the committed `context_limits.code_section` pin.\nEverything else is replaced whole — lists, scalars, an explicit empty mapping, a `null`, and any\nchange of type. `task_board` is the single atomic mapping key: it is a coherent contract (`type` +\n`project` + `create_target`/`done_target` + `options`), so a later layer replaces it completely\nrather than per subfield. Replacement of a value that an earlier layer also set is **shadowing**.\nInspect the effective policy, the source of each key, and shadowed sources with:\n\n```bash\nreviewer config show --repo group/service --branch main --json\n```\n\n`sources` and `shadowed` are keyed by the **dotted path to the leaf**\n(`context_limits.code_section.max_files`), not by the top-level policy key — otherwise \"the\nsubsection was shadowed\" is indistinguishable from \"there was no subsection\". `task_board` and any\nkey a layer set as a whole (to a scalar or `null`) keep a single entry on the key itself. In the\ntext output a key whose leaves all come from one layer stays a single `source:` line; when layers\ndiffer the line reads `source: mixed` and is followed by one line per leaf.\n\nThe committed layer is fetched at the selected ref, so review/config resolution never reads an\nuncommitted worktree `.review.yml`.\n\nIt is read **from a local clone whenever one is usable**, and only otherwise through the hosting\nAPI. `config show` uses `--path <clone>` if given and the current directory otherwise; the MCP\nserver uses the clone path recorded by `reviewer index` (which already runs from a clone). A\ncandidate is accepted only if it is a git repository whose remote matches the target repo — a clone\nwith **no** recognizable remote is accepted too, which is exactly the case where the committed layer\nwas previously unreachable. If the ref does not resolve in the clone (branch not fetched), the read\nfalls back to the API rather than silently reporting an empty layer. The report states which path\nwas taken:\n\n```bash\nreviewer config show --repo group/service --branch main --path /srv/clones/service\n# committed: git-blob     ← resolved without a single network call\n```\n\nIn JSON the same value is the `committed_source` key (`git-blob` / `vcs`); the clone path itself is\nnever printed. The label names the object that was read — the committed git blob at the ref, never\nthe file in the clone's working tree.\n\nBecause of that distinction, `config show` also reports whether the working tree has drifted away\nfrom that blob. The `worktree_drift` key (JSON: `{\"status\": ..., \"keys\": [...]}`; text: a\n`worktree_drift:` line followed by the diverging keys) is present only when the committed layer was\nactually read from the clone. Statuses: `clean` (no difference in values — a comment-only or\nreformatting edit is not drift), `drifted` (diverging leaf keys are listed, **never their values**),\n`absent_in_worktree`, `absent_in_blob`, `ref_not_head` (the ref does not resolve to the clone's\nHEAD, so no comparison was made), and `unknown` (the diagnostic itself failed; it stays silent in\nthe text output). Drift is a warning only: the effective policy still comes entirely from the\ncommitted blob, and the exit code is unaffected.\n\nTo copy a safe committed policy into the repo-specific home\nlayer without modifying the committed file, run:\n\n```bash\nreviewer config migrate --repo group/service --branch main\n```\n\nMigration is non-destructive: an equivalent destination is a no-op, while a differing destination\nis reported as a conflict and left unchanged. Home files with credential-like keys are rejected as\npolicy layers and their values are never displayed; keep credentials in server environment instead.\nHome configuration belongs to the OS account running reviewer. On a shared service account it can\nsilently affect that account's workloads, so use committed `.review.yml` for team-visible policy and\nrestrict the service account's home configuration permissions.\n\nUse `configure-review` to update context fields without clobbering unrelated keys. It\nrecommends the per-repo home target first, or can explicitly update the committed `.review.yml` for\nteam-visible policy.\n\n### Task boards\n\nBoard selection is generic and registry-driven. Credentials come from server env; `.review.yml`\ncontains only non-secret metadata:\n\n```yaml\ntask_board:\n  type: <registered-provider>\n  project: PRI\n  key_pattern: \"[A-Z]+-\\\\d+\"\n  url_template: \"https://tasks.example/{code}\"\n  create_target: Backlog\n  done_target: Done\n  options:\n    <provider-option>: <discovered-value>\n  sync_filter:\n    max_age_days: 180\n    include_archived: false\n```\n\nThe repo block wins; an explicit empty `task_board:` disables board work. If the block is absent,\nthe server may use a **non-secret deploy-wide fallback**. Calls use configured registry credentials\nwithout returning them.\n\n`sync_filter` is a generic sibling of provider `options`. By default `max_age_days` is absent (no\nage limit) and `include_archived: true`. Age uses task last-modified time with an inclusive cutoff,\nso a task exactly at the boundary remains eligible. An unknown age is not filtered by age. Only\nwhile `include_archived: false`, unknown archive does not itself exclude the row and an archive\nwarning is emitted only then. Age filtering runs first and may still exclude the row; in that case\narchive uncertainty is not counted or warned. Archive is separate from terminal/done state.\nRepositories with the same `task_board.project` share one task corpus. Retention never deletes\nimplicitly: purge is explicit. A filter change backfills newly eligible tasks on the next successful\nfull sync.\n\nThe server-side flow is **store-first**:\n\n1. `sync_board` enumerates and normalizes tasks, then stores vectors and task-graph metadata under\n   `tasks:<type>:<board>`.\n2. Skills call `get_task(key, project=...)`; linked tasks/PRs/code come from task context tools.\n3. Client models never enumerate the provider directly and never send credentials.\n\nThe MCP server currently exposes **42 tools**, including the native-subtask batch operation.\n\nLegacy aliases remain **legacy metadata for older clients** for one compatibility window:\n`TASK_BOARD_API_KEY → YOUGILE_API_KEY` and\n`TASK_BOARD_API_BASE → YOUGILE_API_BASE`. New deployments should use registry-declared\nprovider credentials. See [docs/board-providers.md](docs/board-providers.md) for the current\nprovider matrix, target discovery, options, setup, and credential rotation.\n\n### Observability and tuning\n\n`reviewer serve` exposes review history and traces through the optional web extra. Summary depth,\ntop-k threshold, graph backend, and retrieval ceilings change cost/recall trade-offs; start with\ndefaults and tune only after observing real misses or excessive context.\n\nReview cost accounting uses two independent channels. The plugin's `PreToolUse` hook\n(`plugin/hooks/review_cost.py`) reads the Claude Code session transcript client-side and writes a\nper-stage token usage sidecar that `publish_review` reads server-side, weighting token buckets\n(fresh input, output, cache write, cache read) rather than summing raw token counts. The step-by-step\ntool-call trace (`review_steps`, shown on the run's trace page) is recorded entirely server-side and\nindependently of the hook. `total_cost` and the per-stage breakdown are weighted, unitless scores —\nnot dollar amounts.\n\n## CLI reference\n\n| Goal | Commands |\n|---|---|\n| Configure and integrate | `init`, `install`, `install-skills`, `update` |\n| Validate environment | `check` |\n| Manage local infrastructure | `start`, `stop` |\n| Manage indexes | `index`, `status`, `search`, `migrate-branches`, `gc` |\n| Run observability UI | `serve` |\n| Start MCP directly | `reviewer-mcp` |\n\nUse `reviewer COMMAND --help` for the current option set. `status` does not spend Voyage tokens;\n`search` and indexing do.\n\n## Skills reference\n\nThe examples below use Claude-style `/rag-reviewer:...` invocation. Codex exposes the same\nnamespaced skills with `$rag-reviewer:...`.\n\n### `review-pr` — full PR review\n\n- **When:** find correctness, security, performance, and maintainability issues in a PR.\n- **Invoke:** `/rag-reviewer:review-pr owner/repo#123 --dry-run`.\n- **Needs:** reviewer MCP, VCS access, stores, and preferably a fresh base index/graph.\n- **Reads/writes:** reads PR/code/task context; publishes through `publish_review` unless dry-run.\n- **Result:** grounded inline comments plus a summary; deterministic publish handles dedup.\n\n### `solve-task` — task to development brief\n\n- **When:** start implementation from a key such as `PRI-220` or a free-text request.\n- **Invoke:** `/rag-reviewer:solve-task PRI-220`.\n- **Needs:** reviewer MCP; board context is optional and the pipeline continues board-less.\n- **Reads/writes:** reads task/code context and writes one brief under `docs/superpowers/briefs/`.\n- **Result:** a compact brief handed to brainstorming; implementation happens in later skills.\n- **Context gathering:** one server-side call, `prepare_task_context`, replaces the former\n  `reviewer status` → `sync_board` → `get_task` → `search_*` chain — preflight, board warm-up, the\n  task itself, linked/similar tasks, relevant subsystems, and code all come back in a single\n  payload. Fail-open semantics are preserved: anything unavailable (stale index, missing board,\n  empty search) is reported per-section in `gaps` instead of aborting the skill. Graph expansions\n  (`get_related_symbols`, `callers`, `implementations`, `family`, …) and `get_pr_diff` stay\n  separate calls made at the LLM's discretion, since they depend on what the brief turns up.\n- **An unavailable source is a signal, not silence:** when a source is unreachable, the first\n  failing section short-circuits the rest *of that source*: every remaining section gets its default\n  plus a `gaps` entry, instead of each paying its own 30-second pool timeout. The breaker is keyed\n  by source — `postgres`, `graph`, `embedder` — so a dead embedder never cancels sections that only\n  need Postgres, and vice versa. The *class* of unavailability is decided by exception type\n  (`is_storage_unavailable`, `is_embedder_unavailable`), but the cause *within* the storage class —\n  a wrong password or a missing database, as opposed to stopped containers — is decided by text\n  (`classify_storage_failure`): a failure during connection setup gives libpq nothing to return, so\n  SQLSTATE is empty and there is no code to branch on. The one detail decided by type instead is an\n  exhausted connection pool: `PoolTimeout` stays inside the storage class (removing it would cost\n  the breaker its ability to trip on the first failure) but may carry `cause_detail: pool_exhausted`,\n  because the containers are up and the real cure is a bigger pool or less concurrency. The type\n  alone is not enough there: production only reaches Postgres through the pool, and a stopped\n  container surfaces as the very same `PoolTimeout`, so the branch adds one observation — a single\n  direct `psycopg.connect(dsn, connect_timeout=2)` outside the pool. Connected means the pool really\n  is busy; failed means the *probe's* exception is classified instead, so a stopped container keeps\n  its `reviewer start` and a stale password still reads as `auth_failed`. Raw text only\n  ever reaches the caller when the cause couldn't be named, and even then only redacted\n  (`reviewer/storage_health.py`). Every `gaps` entry carries `cause` (`storage_unavailable` |\n  `embedder_unavailable` | `unknown`), `cause_detail` (`auth_failed` | `missing_database` |\n  `pool_exhausted` | `null`), and `remedy` — empty not only for remote storages but also once a\n  cause is named, since the containers are already up and the cure doesn't apply — so the skill\n  branches on machine-readable fields rather than on prose. A dead embedder is its own class, not a\n  detail of the storage one: Voyage is not a storage, and `reviewer start` does not fix it. Voyage\n  throttling (`RateLimitError`) is deliberately excluded — on the free tier it is a normal state\n  that `with_voyage_retry` already handles — and so are request-validation failures\n  (`InvalidRequestError` 400, `MalformedRequestError` 422, `VideoProcessingError`): a rejected\n  request is not a dead service, and treating it as one would silently gut half the task context. The server never starts containers — it names the cure,\n  and `solve-task` asks the user whether to run it. The same flag stops `index_batch` from walking a\n  dead pool task by task and from spending Voyage quota it has nowhere to store.\n- **Multi-query retrieval for `code`:** the `code` and `test_exemplars` sections are searched with a\n  *set* of subqueries, not one query over the whole task text. Subqueries are extracted\n  deterministically (`reviewer/mcp/subqueries.py`: list items under \"what to do\"/\"acceptance\"\n  headings, plus a pool of technical identifiers), capped at 20, embedded in a single Voyage batch,\n  run through hybrid search one by one, and merged with RRF. RRF is the *final* ranker here — no\n  reranker and no cliff cutoff, because the cliff scored against that same multi-topic query and\n  collapsed the output to the floor. Each block's text is trimmed on a line boundary so one huge\n  chunk cannot burn the whole render budget; the trim uses the per-file file budget\n  (`CodeSectionLimits.chars_per_file`, PRI-256) rather than a standalone module constant — the\n  earlier `MAX_BLOCK_CHARS` constant was removed once the file budget took over that role. The\n  public `search_codebase` tool stays single-query and unchanged, as does `Retriever.search_base`;\n  the `subsystems` section still gets one query.\n- **Startup survey:** one `AskUserQuestion` panel asks three things before anything else — the\n  brief model tier (`cheap`/`mid`/`premium`), the interaction mode, and the execution strategy.\n  No answer, or a headless run, applies the defaults `mid` / `normal` / `subagent` without\n  blocking.\n- **Interaction modes:** `normal` — brainstorming questions plus spec and plan approvals;\n  `auto` — questions asked, approvals dropped; `full-auto` — no questions, the recommended option\n  taken at every fork, approvals dropped. In every mode the spec and the plan are still written,\n  self-reviewed and committed. `full-auto` still asks before `git push`, opening a PR, or writing\n  to the board.\n- **Execution strategies:** `inline` (executing-plans), `subagent` (subagent-driven-development),\n  `lite` (`plugin/skills/_profiles/execution-lite.md` — one reviewer per group of up to 3 tasks\n  sharing files, a 3-round fix cap, a mandatory final whole-branch review), and `auto` (resolved\n  after the plan by an ordered rubric: risk signals or >8 tasks or >10 files → `subagent`;\n  ≤3 tasks and ≤3 files → `inline`; otherwise `lite`).\n- **Run state:** the chosen mode and strategy are written to `.superpowers/solve-task/<KEY>.md`,\n  which is git-ignored — never to the brief, the spec, or the plan.\n\n### `ask` — grounded codebase Q&A\n\n- **When:** ask where code lives or how a subsystem works.\n- **Invoke:** `/rag-reviewer:ask how does index freshness work?`.\n- **Needs:** a built base index and graph.\n- **Reads/writes:** reads repository context and local files; does not modify or review code.\n- **Result:** a Russian explanation with real `path:line` citations.\n\n### `pr-walkthrough` — human reading guide\n\n- **When:** orient a human reviewer without running a bug review.\n- **Invoke:** `/rag-reviewer:pr-walkthrough owner/repo#123`.\n- **Needs:** reviewer MCP, PR access, base index, and graph.\n- **Reads/writes:** reads impact/diffs/callers; posts only on explicit request.\n- **Result:** centrality-first reading order, per-file summary, and grounded impact notes.\n\n### `performance-review` — performance-only review\n\n- **When:** inspect a diff for repeated work, N+1 I/O, asymptotics, batching, caching, or memory.\n- **Invoke:** `/rag-reviewer:performance-review`.\n- **Needs:** a diff/PR or explicit change scope; reviewer context is fail-open.\n- **Reads/writes:** reads the selected changes and nearby context; does not publish by itself.\n- **Result:** only concrete performance findings, with assumptions stated.\n\n### `maintainability-review` — maintainability-only review\n\n- **When:** inspect complexity, readability, duplication, boundaries, and repository conventions.\n- **Invoke:** `/rag-reviewer:maintainability-review`.\n- **Needs:** a diff/PR or explicit change scope plus repository guidance.\n- **Reads/writes:** reads changes and nearby patterns; does not change behavior.\n- **Result:** focused simplification findings, excluding unrelated correctness/performance advice.\n\n### `create-task` — create a canonical board task\n\n- **When:** file a grounded task on the configured board.\n- **Invoke:** `/rag-reviewer:create-task describe the requested change`.\n- **Needs:** registered board config, discovered create target/options, and project credentials.\n- **Reads/writes:** reads code for evidence; calls `create_task` only after explicit confirmation.\n- **Result:** canonical body, task key/URL, and a refreshed task corpus.\n\n### `decompose-task` — create native child tasks from one parent\n\n- **When:** split an existing board task into grounded, independently actionable native children.\n- **Invoke:** `/rag-reviewer:decompose-task PRI-224`.\n- **Needs:** a stored parent, configured board, authoritative `native_subtasks` capability, task\n  context, similar tasks, and relevant code from `search_codebase`.\n- **Board config:** inspect the repository `task_board` key once. A present null/empty/disabled\n  explicitly disables board work and never calls deploy-wide `get_board_config`. Only an absent\n  repository key may call `get_board_config` once; a mapping freezes generic `type`, `project`, and\n  `options` for the entire flow.\n- **Preview/confirmation:** shows the provider, parent, idempotency key, and complete canonical body\n  of every child, then asks for one explicit confirmation of the whole preview; no earlier write.\n- **Write/verification:** sends exactly one confirmed initial batch. Every actually attempted batch\n  write is verified regardless of status (`ok`, `partial`, `error`, or timeout) before declaring\n  its outcome or offering recovery.\n- **Verification:** performs exactly one project-scoped sync, re-reads the parent with `get_task`\n  and graph/context with `get_task_context` even when no child keys were returned, and point-reads\n  every returned child key with `get_task`.\n- **Recovery:** partial, timeout, or error recovery is never automatic. The skill preserves and\n  reports `status`, `category`, and `retryable`. After verification, only transport timeout or\n  unknown outcome or `retryable=true` reaches a new explicit user choice between exact retry or\n  stop; `retryable=false`, and `unsupported`, `conflict`, and `parent_not_found` stop without retry.\n  Exact retry replays the same full payload, order, and idempotency key; it never mints a new key,\n  never edits wording, and never sends only the remainder.\n- **Result:** created/attached/unattached/pending children and warnings, reported without guessing.\n\n### `finish-task` — close a task after its PR\n\n- **When:** a PR exists and the board task should be linked and completed.\n- **Invoke:** `/rag-reviewer:finish-task PRI-220 https://github.com/owner/repo/pull/123`.\n- **Needs:** task key, PR URL, registered board config, and discovered done target/options.\n- **Reads/writes:** after explicit confirmation, appends the PR idempotently, updates the task,\n  prepends a task backlink to the PR body, and re-syncs.\n- **Result:** done state plus `already_closed`/`task_link_status` (`added` | `already_present` |\n  `failed`) reporting without duplicate links; `task_link_added` keeps its old meaning\n  (\"written just now\").\n\n### `report-bug` — report a defect of reviewer itself\n\n- **When:** a reviewer MCP tool broke its own documented contract, a skill step was impossible with\n  the available tools, a stated invariant failed, or a reviewer frame appeared in a traceback.\n  Problems of the user's project (environment, external services, permissions, their own code) are\n  deliberately out of scope: the channel is only worth having while it stays silent on them.\n- **Invoke:** `/rag-reviewer:report-bug`.\n- **Needs:** nothing beyond the MCP server; a GitHub token only for the publishing path.\n- **Reads/writes:** the server triages the symptom class, anonymizes every text field\n  deterministically in Python (source fragments, absolute paths, repo/branch/file names, task keys\n  and board URLs, self-hosted hosts, e-mails, tokens) and assembles the issue for\n  `mimfort/rag_for_git`. **What leaves your machine** is the anonymized narrative plus an\n  Environment block of *shape only*: orchestrator and subagent models, mode, CLI and OS, reviewer /\n  plugin / Python versions and install mode, registered board type, VCS type and whether it is\n  self-hosted (never the host), graph backend, index presence and drift as a number, and integer\n  counts of clusters/files/findings/tasks. The exact final text is shown before anything is sent,\n  and the Environment block can be trimmed line by line or dropped entirely without blocking the\n  report.\n- **Approval:** publication happens **only** after an explicit human yes, and never in headless,\n  cron or background runs — this is enforced server-side, not by the prompt. The issue is created\n  from the user's GitHub account, so their username becomes visible in a public repository; the\n  skill says so before asking. A matching open issue gets a comment instead of a duplicate.\n- **Result:** `published` / `commented` with URLs, or `fallback` with ready-made markdown and a\n  prefilled issue link for manual posting — a failure to report never breaks the session.\n- **Automatic trigger:** a `PostToolUse` hook watches reviewer tool results and recognizes two\n  shapes deterministically — a traceback with `reviewer/*` frames, and a `status` value outside a\n  tool's documented set — so noticing a defect is not left to the model's attention. Routine\n  failures are checked **first** and always win: unavailable stores, missing keys or tokens, board\n  rate limits, 401/403/404, network timeouts, a missing or stale index, and an untracked branch\n  never produce a nudge. Invariant violations (idempotency, dedup, counters) stay model-noticed:\n  they are invisible in a single response, and guessing from one call is how a hook turns into\n  noise. The nudge carries only the shape of the failure, fires at most once per symptom per\n  session, and costs nothing when nothing is wrong.\n- **Switch:** `bug_reports: false` in a repository's `.review.yml` disables the channel and the\n  hook for that repository, `REVIEW_BUG_REPORTS=false` for the whole deploy.\n\n### `sync-codebase` — build or update the base index\n\n- **When:** initialize an index, refresh stale code, or rebuild the graph.\n- **Invoke:** `/rag-reviewer:sync-codebase --path /srv/repo --ref main`.\n- **Needs:** git clone, `uvx`, reviewer services, Voyage, and optional SCIP.\n- **Reads/writes:** reads the selected git ref and writes branch-scoped vectors/graph nodes.\n- **Result:** incremental index report; failures name the missing prerequisite.\n\n### `sync-tasks` — warm task vectors and graph\n\n- **When:** synchronize a configured board before task search or solve-task.\n- **Invoke:** `/rag-reviewer:sync-tasks`.\n- **Needs:** use `reviewer init`, configure the selected provider as documented in\n  `docs/board-providers.md`, then validate it with `reviewer check`.\n- **Reads/writes:** calls idempotent server-side `sync_board` in repo mode with canonical repo and\n  tracked branch. The server resolves effective policy; the client does not reconstruct it. It reads\n  the board and does not write back. Policy errors never retry as an unfiltered explicit call.\n- **Result:** `eligible`, `filtered_by_age`, `filtered_archived`, `age_unknown`, `archive_unknown`,\n  `filter_applied`, `filter_fingerprint`, `filter_source`, `by_board`, `purge`, and `warnings`;\n  missing config remains board-less/fail-open.\n\n### `summarize-subsystems` — GraphRAG subsystem summaries\n\n- **When:** build or refresh the architectural prior used by Q&A and PR walkthroughs.\n- **Invoke:** `/rag-reviewer:summarize-subsystems`.\n- **Needs:** a fresh base index, code graph, reviewer MCP, and confirmation of cluster depth.\n- **Reads/writes:** reads skeletons of only added/changed files via `get_file_skeletons` (job's\n  input is a skeleton, not the source), batched up to 15 paths per job, reuses stored per-file\n  fragments, and atomically writes fragments together with the cluster summary.\n- **Result:** сводки и метрики `created`/`reused`/`removed`/`moved`,\n  `deferred`/`raced`, `fragments_pruned` и `embedded`.\n- **Payload:** the cluster listing runs in compact, paginated mode\n  (`compact=True`, `offset`/`limit`): metadata plus `added`/`changed`/`removed`/`moved` counters,\n  no paths and no fingerprints, so its size grows with the number of clusters rather than files\n  (10 922 B compact vs 97 530 B full on this repository; the full format itself was 106 878 B\n  before PRI-229). Per-cluster file detail comes from `get_subsystem_summary_work`. In full\n  format `files` lists only unchanged files — the delta lists are not repeated there.\n\nПервый полный прогон после обновления создаёт fragments для всех текущих файлов, но не удаляет\nстарые сводки: каждый кластер заменяется только после успешной атомарной записи нового bundle.\nПри настроенном cap bootstrap может занять несколько проходов. Freshness считается по\nskeleton-коду, поэтому правка только тела функции намеренно остаётся невидимой, пока не изменится\nskeleton. Layout identity — canonical token от default `summary_cluster_depth` и\nнормализованных `summary_cluster_depth_overrides`: смена любого из них принудительно пересобирает\nвсе fragments, даже если default depth прежний. Частичный или ограниченный cap-ом прогон не\nзапускает prune; optimistic race (`stored=false`) тоже считается отложенным, не успехом, и\nзапрещает prune в этом проходе. Полный проход передаёт в prune token и точную карту\n`cluster_key → source_hash`; сервер повторно выводит layout и под advisory lock проверяет каждую\nsummary и same-generation fragment coverage до удаления сирот и финализации state. Embedding\nbackfill пишет вектор только по exact CAS `source_hash + title + summary`, поэтому конкурентная\nперезапись текста не получает устаревший вектор и не увеличивает `embedded`.\n\n### `configure-review` — update layered policy and branches\n\n- **When:** tune tracked branches, ignored paths, retrieval limits, summary clustering, or board\n  metadata.\n- **Invoke:** `/rag-reviewer:configure-review`.\n- **Needs:** a git repository; MCP and databases are not required for baseline analysis.\n- **Reads/writes:** reads tracked Python structure/history and changes approved YAML fields in either\n  `home:repos/<owner>/<name>.yml` or committed `.review.yml`; branch values always go to the home\n  per-repo YAML.\n- **Result:** preserved foreign keys/comments plus exact rebuild guidance.\n\n## Operations, troubleshooting, and limitations\n\n### Health checks\n\nUse these before investigating application behavior:\n\n```bash\nreviewer check\nreviewer status /path/to/repo --json\ndocker compose ps\n```\n\n`reviewer check` validates configured credentials and service connectivity without spending\nVoyage quota. `status` compares the indexed SHA with the selected local ref and reports chunks,\ngraph nodes, subsystem summaries, and commit drift for each tracked branch.\n\n### Index freshness and recovery\n\n- `drift == 0`: the base index matches the selected ref.\n- `drift > 0`: run `reviewer index /path/to/repo --ref BRANCH` after considering Voyage cost.\n- `drift == null` or zero chunks: the branch has no usable base record; build it explicitly.\n- Missing `IMPLEMENTS` edges: ensure SCIP is installed and rebuild with the SCIP backend.\n- Orphaned `pr:N` overlays or expired persisted sessions: run `reviewer gc`.\n\nBase indexes track committed refs, not working-tree edits. During planning or review, read\nuncommitted files directly from disk.\n\n### Common failures\n\n| Symptom | Likely cause | Next action |\n|---|---|---|\n| `reviewer check` reports Postgres/Neo4j unavailable | Default stores are not running or DSNs differ | Run `docker compose -f ~/.config/rag-reviewer/docker-compose.yml up -d`, then repeat `reviewer check` |\n| Voyage returns 429 | Free-tier RPM/TPM quota is exhausted | Wait for the quota window; rerun incremental indexing rather than deleting the index |\n| PR is skipped | Its target branch is not tracked for this repository (see `reviewer config show`), or draft policy skips it | Inspect `prepare_review` reason; if the target is intentional, add the branch via the per-repo home layer (or `REVIEW_BRANCHES` fallback), not just policy |\n| `config show` reports a `skipped` `.review.yml` layer and exits non-zero | The committed policy layer could not be fetched (no network/token, 404) or could not be parsed | Home layers are still applied — check the reported `category`/`http_status`; fix the remote or the committed YAML. Review, indexing, and migration stay loud and fail instead. A home layer with a forbidden credential key is also reported as `skipped` and exits `1`, even though the layer is simply excluded from resolution |\n| Task lookup is empty | Board is disabled/unconfigured or the corpus is cold | Validate [board setup](docs/board-providers.md), then run `/rag-reviewer:sync-tasks` |\n| Q&A misses new local code | Base index contains only a committed ref | Read the local file or commit/index the intended branch |\n| AI client cannot see new skills | Client session predates installation | Start a New Chat/new CLI session; use Reload Window in an IDE |\n\nSecondary context is deliberately fail-open: an unavailable graph, board, subsystem prior, or\nhistorical PR diff should reduce context and produce a warning, not invent data.\n\n### Web admin\n\nThe optional web UI shows review runs, findings, traces, and aggregate statistics:\n\n```bash\npip install -e \".[web]\"\ncd web/frontend && npm install && npm run build && cd ../..\nreviewer serve\n```\n\nThe **Quality** page shows the trend of the solve-task brief quality metric across tasks: median\ncore-recall (precision is plotted per task on the trend chart; it has no median of its own), a bulk\nsubsample (tasks with `expected_core >= 10`, the `BULK_CORE_THRESHOLD`) with a horizontal line for\nthe offline baseline for before/after comparison, and a breakdown of misses by taxonomy.\nThe data source is the `brief_quality` table, populated on every real `publish_review` call\n(written by `MCPReviewService`, not a separate process). If a task's brief is missing, or has no\n`## Relevant code` section at all, the measurement is skipped — no point shows up on the chart\ninstead of a zero or an error. A section that exists but is empty is not a skip: it is a valid\nmeasurement with `predicted = 0`.\n\nThe container keeps its internal listen port separate from the published loopback port. Build it\nonce and choose both at runtime (replace `database` with a Postgres host reachable from the\ncontainer):\n\n```bash\ndocker build -f web/Dockerfile -t rag-reviewer-web .\ndocker run --rm \\\n  --env PG_DSN=postgresql://reviewer:reviewer@database:5432/reviewer \\\n  --env REVIEWER_WEB_PORT=8080 \\\n  --publish 127.0.0.1:18000:8080 \\\n  rag-reviewer-web\n```\n\nThe Compose service is opt-in, so ordinary `docker compose up` still starts infrastructure only:\n\n```bash\ndocker compose --profile web up -d web\nREVIEWER_WEB_PORT=8080 REVIEWER_WEB_PUBLISH_PORT=18000 \\\n  docker compose --profile web up -d web\n```\n\nWithout overrides, both the internal and published ports default to `8000`.\n\nSet `WEB_ADMIN_USER` and `WEB_ADMIN_PASSWORD` before exposing it beyond localhost. Store and API\nerrors are reported without preventing the process from starting where fail-soft behavior is safe.\n\n### Security\n\n- Keep Voyage, VCS, board, database, and web-admin credentials in server env, never `.review.yml`.\n- Use least-privilege VCS tokens; publishing and `finish-task` perform external writes.\n- Review every confirmation gate before comments, board tasks, status transitions, or PR-body\n  changes.\n- Stored copies stay in the configured databases, but code chunks and search text are sent to\n  Voyage; PR diffs and retrieved context are also sent by the AI client to its AI model provider.\n- External provider calls require network access; ordinary unit tests do not.\n\n### Known limitations\n\n- Python is the supported analysis language; SCIP gives the most accurate graph.\n- Without SCIP, tree-sitter provides a useful but name-based `CALLS` graph plus class-level\n  `IMPLEMENTS` from syntax; method-level override `IMPLEMENTS` coverage stays SCIP-only.\n- GitHub permits inline comments only on commentable diff lines; other findings appear in summary.\n- Full indexing can hit Voyage free-tier limits; updates are incremental and reuse embeddings.\n- The base index is branch-scoped and blind to uncommitted working-tree changes.\n- OAuth loopback flows are not supported in headless/SSH integrations; use documented PAT/API-key\n  credentials.\n- Board work is optional. Missing provider configuration keeps task-aware skills board-less rather\n  than blocking code retrieval.\n\n## Development\n\nCreate an isolated environment and install development dependencies:\n\n```bash\npython -m venv .venv\n.venv/bin/pip install -e \".[dev]\"\ngit config core.hooksPath .githooks\n```\n\nThe last command enables the tracked `pre-commit` hook: it runs `ruff check` on staged `.py`\nfiles and blocks the commit when they are not clean. Git cannot enable hooks automatically, so\nevery clone opts in once. Bypass a single commit with `git commit --no-verify`.\n\nUnit tests prohibit external and localhost sockets and exclude integration tests by default:\n\n```bash\n.venv/bin/pytest -q\n.venv/bin/ruff check .\n```\n\nRun integration services in the isolated test profile:\n\n```bash\ndocker compose --profile test up -d --wait paradedb-test neo4j-test\n.venv/bin/pytest -q -m integration\ndocker compose --profile test rm -sfv paradedb-test neo4j-test\n```\n\nNever use `docker compose --profile test down -v`: the test and development services share a\nCompose project, so that command can remove development volumes.\n\n### solve-task metrics (offline)\n\nAn offline harness measures the cost of the solve-task stage and retrieval\nquality over the accumulated brief corpus (`docs/superpowers/briefs/`), stores a\nhistory of snapshots and compares runs. The retrospective commands need no\nPostgres, Neo4j or network — local git only; `replay` is the exception, it needs\nlive retrieval.\n\n```bash\npython -m eval.solve_task_metrics snapshot            # recompute metrics, store a snapshot, refresh the report\npython -m eval.solve_task_metrics stats --last 10     # trend of the latest snapshots as a table, no recompute\npython -m eval.solve_task_metrics compare --back 1    # deltas of the latest snapshot against N steps back\npython -m eval.solve_task_metrics forecast            # core-recall forecast with a spread\npython -m eval.solve_task_metrics replay              # re-run retrieval over the corpus (baseline)\npython -m eval.solve_task_metrics replay --variant limits --set search_codebase.ceiling=25 --baseline last   # A/B against a stored snapshot\n```\n\n**`replay`** rebuilds the candidate set by calling production retrieval with the\ntask text from the store (not the brief text) and compares configuration\nvariants: the `eval/replay_report.md` report shows the delta both per aggregate\nand per task, snapshots go to `eval/replay_history.jsonl`. It requires Postgres,\nNeo4j, Voyage and a built base index. The `replay` line is **not comparable** to\nthe `snapshot` line: snapshot counts the paths an LLM selected, replay counts the\nwhole retrieval output.\n\nCost is measured in weighted input-equivalents (`output ×5`, `cache-",
  "bytes": 60000,
  "sha": "4ac0ae7910f4c3e390a240698cf14d06b428afd84b379d3fd8cf701fc090386e",
  "repo_slug": "mimfort/rag_for_git",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_mimfort_rag_for_git_5fb4b876/readme"
}