feat: per-repo review policy (gating, auto-triggers, behavior overrides) #44
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Captured from a design conversation; not yet a ready-to-build spec, but enough structure to come back to.
Goal
Let repo admins customize how the bot behaves and how its outcomes interact with the PR, on a per-repo basis. Today everything is global (one set of
model_configs, one tier-prompts pair, no concept of "this repo wants something different"). The proposal collects three orthogonal axes under one per-repo policy row.Three orthogonal axes
1. Gating policy — what commit status the bot posts on completion
Single status context:
pr-reviewer. Branch protection on the target repo listspr-revieweras a required status; merge is blocked while pending and depends on policy at completion.Initial menu (one of):
pendingwhile reviewing →successregardless of verdict. "Block until the bot finishes."successonlgtm/approve,failureonflag/request_changes.PR-level overrides (e.g. labels like
pr-reviewer:advisoryto soften gating on a specific PR) is a logical follow-up once the per-repo machinery exists.2. Auto-trigger — when to escalate quick → full without a human asking
Boolean per trigger (checkbox UI), evaluated at quick-scan completion:
auto_full_on_complex— quick scan returned complexity=complex.auto_full_on_flag— quick scan returned verdict=flag.UX wrinkle to decide when implementing: today a quick-then-full produces two separate bot comments. Either keep the chatty behavior, or edit the quick comment to "escalating to full review" and start a new pending → completed cycle for the full. The latter reads better.
3. Behavior overrides — per-repo customization of what "quick" and "full" mean
Examples to anchor the shape:
customer_data/*." A per-repo path-exclusion list applied during context fetch (both for the LLM-discovery whitelist and for any guidance/listing reads).This axis implies an architectural pivot worth flagging: the current "all enabled
model_configsfan out on every PR globally" model assumes uniform behavior across repos. Once customization lands, you may also want per-repo config selection (repo X uses configs A+B, repo Y uses C). Doable but a bigger change than just adding columns.Storage sketch
One new table:
Repos with no row default to current global behavior. Admins manage rows from the dashboard; primary edit surface is a card per repo with checkbox/radio groups.
Open questions
prompt_full_overridewould shadow the model_configs-level override. Which wins? Probably repo override > config override > global default. Worth explicit precedence rules.pr-reviewer:advisory,pr-reviewer:strict)? PR description directives? Repo-level config that allows a specific label set?model_configssee that customization, or do we move to per-repo config selection? Latter changes A/B-comparison semantics.Suggested implementation order
If/when this is built, the natural sequence is roughly:
Addition to axis 3 (behavior overrides): the discovery + extract pipeline itself has knobs that are reasonable per-repo customization targets, beyond just on/off and path exclusions. The current globals all live in
internal/repocontext/andinternal/reviewer/tools.go; any of them could become a per-repo override:MaxFragmentsPerRequest(5),MaxExcerptChars(8k),MaxListingDirs(10),MaxListingEntriesPerDir(50). Repos with deep monorepos may want larger listing budgets; repos with tight token budgets may want smaller fragment caps.DiscoveryMaxTokens(1024) andExtractMaxTokens(4096) for the sub-passes.discovery_prompt_overrideandextract_prompt_overrideso the discovery LLM can be told repo-specific norms ("this is a Kubernetes manifests repo; prefer pulling sibling YAML files for comparison" vs. "this is a Go monorepo; prefer pulling type definitions and interface declarations").GuidanceProbeListis hard-coded toCLAUDE.md,AGENTS.md,AGENT.md,.cursorrules,.github/copilot-instructions.md. Repos with house standards under different filenames (e.g.STYLE.md,docs/REVIEW_RULES.md) could extend this list per-repo.schema/,docs/conventions/) included in every discovery prompt's listings regardless of whether the diff touches them, so the LLM always has that orientation context.These aren't all needed up front — most repos will work fine with the current globals — but the per-repo policy table should be designed knowing this category exists, so the schema doesn't paint us into a corner. Concretely: keep
repocontext-related overrides nullable, add them lazily as need surfaces, and document the precedence rule (repo override > global default) consistently with the prompt/token overrides above.Storage decision pinned during preflight (step-0) work: observations land in a JSONB column (
review_events.observations, migration 000008). The migration's column comment captures the coupling we agreed on: the move from JSONB to a normalizedreview_observationstable is tied to the move from a free-form to a deterministic tag vocabulary — those decisions are linked, since the relational shape only pays off once "show me reviews where tag = X" is a useful question against a closed enum, and a closed enum is what unlocks per-repo halt rules in the first place. Captured here so the trail's in the issue people will look at when picking up the rule layer.