diff --git a/.gittensory.minimal.yml b/.gittensory.minimal.yml index eff7b3b6b5..9b6b7baa04 100644 --- a/.gittensory.minimal.yml +++ b/.gittensory.minimal.yml @@ -4,7 +4,9 @@ # # Copy this file to your repo root as `.gittensory.yml` and customize from here. # (This filename is not read directly — only `.gittensory.yml` / `.github/gittensory.yml` are.) -# For every supported field, defaults, and examples see `.gittensory.yml.example`. +# Also shipped at config/examples/gittensory.minimal.yml for self-host operators. +# For every supported field, defaults, and examples see `.gittensory.yml.example` or +# config/examples/gittensory.full.yml. # # Safe by default: # - Gate off (enable explicitly when you are ready) diff --git a/.gittensory.yml.example b/.gittensory.yml.example index 02e5762afe..7d9eddbe32 100644 --- a/.gittensory.yml.example +++ b/.gittensory.yml.example @@ -6,9 +6,11 @@ # review engine scores, gates, and comments on its pull requests — as # config-as-code, versioned alongside the project it governs. # -# STARTER TEMPLATES: -# .gittensory.minimal.yml — smallest safe copy-paste starter (gate off, observe-only autonomy) -# .gittensory.yml.example — exhaustive, field-by-field reference (this file's expanded form) +# STARTER TEMPLATES (also shipped under config/examples/ for self-host operators): +# config/examples/gittensory.minimal.yml — smallest safe starter (gate off, observe-only autonomy) +# config/examples/gittensory.full.yml — exhaustive reference (body synced with this file) +# .gittensory.minimal.yml — same minimal starter at repo root +# .gittensory.yml.example — this file # # WHERE IT LIVES (first match wins): # .gittensory.yml → .github/gittensory.yml → .gittensory.json → .github/gittensory.json diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx index 324aff2cb7..985fbe3125 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx @@ -91,13 +91,81 @@ function SelfHostingConfiguration() {

This page covers the environment layer and the shape of the config file. For the full field list — every gate: and settings: key, its default, and what it - does — see Tuning your reviews, and for a complete, - commented, copy-pasteable manifest see{" "} - - .gittensory.yml.example - {" "} - in the repo — the authoritative reference for every field, including several documented only - in its comments (see below). + does — see Tuning your reviews, and for copy-paste templates + see the table below (also shipped inside the self-host image at{" "} + config/examples/). +

+ +

Config templates

+

+ Start from a template instead of reverse-engineering env flags, private-config precedence, + and the parser. Every template uses the same schema for a public repo-root{" "} + .gittensory.yml or a container-private GITTENSORY_REPO_CONFIG_DIR{" "} + mount — only what you put in each file differs. +

+ + + + Keep anti-abuse thresholds, maintainer allowlists, and autonomy dials in the{" "} + private mount — not in a public .gittensory.yml contributors + can read. config/examples/TEMPLATES.md documents the public-vs-private split + and how to apply the templates to gittensory, awesome-claude, and{" "} + metagraphed without committing private policy. Lint before deploy:{" "} + npx tsx scripts/gittensory-config-lint.ts path/to/.gittensory.yml. + +

Authoritative copies in git:

+ +

+ Several gate-only fields are documented only in the full template comments — see below for + the config-as-code blocks with no dashboard equivalent.

Required baseline env

diff --git a/config/examples/README.md b/config/examples/README.md index ed15e1dfa1..be75528955 100644 --- a/config/examples/README.md +++ b/config/examples/README.md @@ -5,6 +5,10 @@ This directory ships **generic, safe** examples for the self-host **private** co contains no real policy, thresholds, logins, or repo names — copy what you need into your own mounted config directory and edit it there (never in this repo). +See **[TEMPLATES.md](./TEMPLATES.md)** for the full template catalog (minimal + exhaustive +`gittensory.yml` starters, public-vs-private usage, and fleet notes for `gittensory`, +`awesome-claude`, and `metagraphed` without committing private policy). + The private config directory is read by `src/selfhost/private-config.ts` and is kept **out of the public GitHub repo** on purpose: contributors can read a public `.gittensory.yml`, so anti-abuse thresholds, maintainer/admin allowlists, autonomy dials, and model/effort settings belong here @@ -22,10 +26,11 @@ ${GITTENSORY_REPO_CONFIG_DIR}/.gittensory.yml # 4. global default, ``` `.yaml` and `.json` are accepted everywhere `.yml` is. Every one of these files uses the **exact -same schema** as the public `.gittensory.yml` — see [`.gittensory.yml.example`](../../.gittensory.yml.example) -at the repo root for the exhaustive, field-by-field reference (not duplicated here, so the two -never drift out of sync). For the smallest safe starter, copy [`.gittensory.minimal.yml`](../../.gittensory.minimal.yml) -to your repo root as `.gittensory.yml` and customize from there. +same schema** as the public `.gittensory.yml` — see [`gittensory.full.yml`](./gittensory.full.yml) +(or [`.gittensory.yml.example`](../../.gittensory.yml.example) at the repo root) for the exhaustive, +field-by-field reference. For the smallest safe starter, copy [`gittensory.minimal.yml`](./gittensory.minimal.yml) +(or [`.gittensory.minimal.yml`](../../.gittensory.minimal.yml)) to your repo root as `.gittensory.yml` +or into your private mount and customize from there. ## Precedence chain diff --git a/config/examples/TEMPLATES.md b/config/examples/TEMPLATES.md new file mode 100644 index 0000000000..292e08dca6 --- /dev/null +++ b/config/examples/TEMPLATES.md @@ -0,0 +1,98 @@ +# Gittensory review config templates + +Copy-paste templates for `.gittensory.yml` — the per-repo review manifest. Every file in this +directory uses the **same schema** whether it lives in a public repo root or a self-host private +mount (`GITTENSORY_REPO_CONFIG_DIR`). + +## Template catalog + +| File | Purpose | +|------|---------| +| [`gittensory.minimal.yml`](./gittensory.minimal.yml) | Smallest safe starter — gate off, observe-only autonomy, no accidental writes | +| [`gittensory.full.yml`](./gittensory.full.yml) | Exhaustive commented reference — every `gate:`, `settings:`, `review:`, and `features:` field | +| [`global.gittensory.yml`](./global.gittensory.yml) | **Private only** — illustrative fleet-wide default for a self-host mount | +| [`repo-override.gittensory.yml`](./repo-override.gittensory.yml) | **Private only** — per-repo overlay deep-merged over `global.gittensory.yml` | + +Canonical copies of the minimal and full templates also live at the repo root as +[`.gittensory.minimal.yml`](../../.gittensory.minimal.yml) and +[`.gittensory.yml.example`](../../.gittensory.yml.example). CI keeps the `config/examples/` copies +in sync with those files. + +## Public repo root vs private self-host mount + +| Layer | Path | Who can read it | Typical contents | +|-------|------|-----------------|------------------| +| **Public** | `.gittensory.yml` or `.github/gittensory.yml` in git | Contributors | `wantedPaths`, test expectations, public review presentation | +| **Private global** | `${GITTENSORY_REPO_CONFIG_DIR}/.gittensory.yml` | Operator only | Shared autonomy baseline, contributor caps, maintainer allowlists | +| **Private per-repo** | `${GITTENSORY_REPO_CONFIG_DIR}/owner__repo/.gittensory.yml` | Operator only | Repo-specific CI context names, AI mode, overrides | + +When **either** a private global or private per-repo file exists, the loader **never fetches** the +public repo file for that review — mount private policy deliberately. See [README.md](./README.md) +for precedence and deep-merge rules. + +**Never commit real private policy** (maintainer logins, thresholds, autonomy dials you do not want +contributors to read) into a public repository. Copy `global.gittensory.yml` into your gitignored +`gittensory-config/` mount and edit there. + +## Quick start + +### Public repo (contributor-visible config) + +```bash +cp config/examples/gittensory.minimal.yml .gittensory.yml +# edit wantedPaths / gate when ready +``` + +### Self-host private mount (operator-only policy) + +```bash +mkdir -p gittensory-config +cp config/examples/global.gittensory.yml gittensory-config/.gittensory.yml +# edit your-admin-login placeholders before going live +# optional per-repo overlay: +mkdir -p gittensory-config/myorg__myrepo +cp config/examples/repo-override.gittensory.yml gittensory-config/myorg__myrepo/.gittensory.yml +``` + +Point `GITTENSORY_REPO_CONFIG_DIR` at that directory (default `/config` in `docker-compose.yml` maps +`./gittensory-config`). + +## Fleet examples (without committing private policy) + +These patterns apply to common JSONbored repos. **Do not copy real maintainer logins or thresholds +into public git** — use the private mount for anything marked *private* below. + +### `JSONbored/gittensory` (dogfooding) + +- **Public** `.gittensory.yml` in the repo: work-area guardrails, test expectations, gate dimensions + contributors should understand. +- **Private** `gittensory-config/` (gitignored locally, operator mount in production): fleet + autonomy, anti-abuse caps, maintainer exemption lists — the same split described in + [`global.gittensory.yml`](./global.gittensory.yml). +- Start from `gittensory.minimal.yml` in the public repo until gate semantics are tuned, then promote + fields into the private global default as you enable autonomous review. + +### `JSONbored/awesome-claude` (public template repo) + +- Prefer **`gittensory.minimal.yml`** or a trimmed public manifest: `wantedPaths`, linked-issue + policy, and advisory gate modes only. +- Keep contributor caps, `autoCloseExemptLogins`, and `autonomy.close: auto` in **private config + only** — this repo is meant to be copied; do not bake operator-specific enforcement into its + public history. + +### `JSONbored/metagraphed` (sibling product repo) + +- Same split as `gittensory`: public manifest for transparent contributor guidance; private mount + for thresholds and maintainer-only rules. +- Use `repo-override.gittensory.yml` when one repo needs different `expectedCiContexts` or + `gate.checkMode: disabled` while sharing a fleet-wide `global.gittensory.yml` baseline. + +## Validation + +Every template in this directory is parsed in CI (`test/unit/config-templates.test.ts` and +`test/unit/selfhost-config-examples.test.ts`). The exhaustive template body is kept identical to +`.gittensory.yml.example` from `# WHERE IT LIVES` onward. Lint a local file before deploy: + +```bash +npx tsx scripts/gittensory-config-lint.ts path/to/.gittensory.yml +``` diff --git a/config/examples/gittensory.full.yml b/config/examples/gittensory.full.yml new file mode 100644 index 0000000000..5a48b72246 --- /dev/null +++ b/config/examples/gittensory.full.yml @@ -0,0 +1,874 @@ +# ============================================================================ +# gittensory.full.yml — exhaustive commented template (#1682) +# ============================================================================ +# +# WHERE TO COPY (pick one): +# PUBLIC REPO — repo root as `.gittensory.yml` (or `.github/gittensory.yml`). Use for work-area +# guidance (`wantedPaths`, test expectations) that contributors may read. +# PRIVATE SELF-HOST — `${GITTENSORY_REPO_CONFIG_DIR}/.gittensory.yml` or a per-repo file under +# `owner__repo/`. Use for anti-abuse thresholds, maintainer allowlists, autonomy, and anything +# contributors must not see or game. See `TEMPLATES.md` for public-vs-private split. +# +# Canonical copy also lives at the repo root as `.gittensory.yml.example` (kept in sync by CI). +# +# STARTER TEMPLATES (this directory): +# gittensory.minimal.yml — smallest safe copy-paste starter (gate off, observe-only autonomy) +# gittensory.full.yml — this file — every gate:/settings:/review:/features: field documented +# global.gittensory.yml — private fleet global default (illustrative placeholders only) +# repo-override.gittensory.yml — private per-repo overlay example +# +# ============================================================================ +# .gittensory.yml — per-repo configuration for gittensory CI & gittensory review +# ============================================================================ +# +# Drop this file at the root of any repo gittensory watches to tune how the +# review engine scores, gates, and comments on its pull requests — as +# config-as-code, versioned alongside the project it governs. +# +# WHERE IT LIVES (first match wins): +# .gittensory.yml → .github/gittensory.yml → .gittensory.json → .github/gittensory.json +# +# PRECEDENCE (most specific wins): +# this file > per-repo dashboard/API settings > built-in safe defaults +# The typed `gate:` block below is an alias for the gate fields and wins over +# the generic `settings:` block for those same fields. +# Hard path guardrails are an explicit config-as-code exception: omitted or +# [] means no path guardrails, never hidden engine defaults. +# +# SAFE BY DEFAULT: with no file and no dashboard row, a repo falls back to a +# quiet, non-blocking profile — gate off, AI review off, slop scoring off, +# comments only to detected contributors, no check-run published. Everything +# here is an explicit opt-in; every value shown is the documented default +# unless a comment says otherwise. Delete any block to inherit the default. +# +# WHO CAN BE BLOCKED: gate modes choose *which* deterministic checks run, never +# *who* gets blocked. A hard block is always confirmed-contributor-gated. +# +# NOTE: some capabilities (safety scanning, CI/full-file grounding, RAG, +# reputation control, the unified comment) are switched on at the deployment +# level by the operator's GITTENSORY_REVIEW_* feature flags AND a per-repo +# cutover allowlist. This file tunes behavior; it does not enable those +# deployment-wide capabilities. +# ============================================================================ + + +# ---------------------------------------------------------------------------- +# 1. FOCUS / GUARDRAILS (focus manifest) +# ---------------------------------------------------------------------------- +# Declares the repo's work areas and off-limits paths. These feed deterministic +# findings (e.g. manifest_blocked_path, manifest_missing_tests) and — when +# `gate.manifestPolicy: block` is set below — can become enforceable blockers. + +# Work areas the maintainer wants. PRs touching these are preferred/encouraged. +# Glob list. Default: [] (no preference). +wantedPaths: + - "src/**" + +# Paths off-limits to contributors. Touching one yields a manifest_blocked_path +# finding (enforceable when `gate.manifestPolicy: block`). +# Glob list. Default: [] (nothing blocked). +blockedPaths: + - "vendor/**" + - ".github/workflows/**" + +# Labels the maintainer prefers on incoming PRs; a missing preferred label is +# surfaced (never blocks). String list. Default: []. +preferredLabels: + - bug + - enhancement + +# How strongly a linked issue is expected on a PR. +# Values: required | preferred | optional. Default: optional. +linkedIssuePolicy: optional + +# Test paths/areas expected to change alongside code. When code changes without +# them, a manifest_missing_tests finding fires. String list. Default: []. +testExpectations: + - "tests/**" + +# Whether opening discovery issues is encouraged for this repo. +# Values: encouraged | neutral | discouraged. Default: neutral. +issueDiscoveryPolicy: neutral + +# PRIVATE review context — never published to any public GitHub surface. +# Use for triage hints the engine should know but contributors should not see. +# String list. Default: []. +maintainerNotes: + - "Private triage context only — never appears on a public comment." + +# Notes explicitly opted into public output. Each line is public-safe filtered; +# unsafe lines are silently dropped. String list. Default: []. +publicNotes: + - "Prefer backend changes that tie to safety or release readiness." + + +# ---------------------------------------------------------------------------- +# 2. GATE POLICY (`gate:`) — refines the deterministic gate +# ---------------------------------------------------------------------------- +# Most gate dimensions are tri-state modes: +# off — the dimension is not evaluated. +# advisory — the finding is surfaced (comment/context) but never blocks. +# block — the finding can become a hard `Gittensory Gate` blocker +# (always confirmed-contributor-gated). +gate: + # Gate master switch. Turns the whole deterministic gate on; the per-dimension + # modes below only refine an already-enabled gate. + # Bool. Default: false (gate off). + enabled: true + + # Review-CHECK publish mode (#2852) — controls ONLY whether/how the "Gittensory Orb Review Agent" + # check-run is created/updated on GitHub. Never affects gate evaluation, comments, labels, audit + # records, or autonomous merge/close — all of those run identically in every mode (the autonomous + # decision engine already excludes the bot's own check-run from the live CI it merges/closes against, + # specifically to avoid a self-deadlock). Takes precedence over the legacy `enabled` boolean above + # when both are set; when unset, `enabled: true` maps to `required` and `enabled: false` maps to + # `disabled` for backward compatibility. + # required — legacy/current behavior: publish/update the check exactly as before. Use this if you + # intentionally keep it as a required branch-protection status check. + # visible — publish/update the SAME check-run, but for UI visibility only. Never add this check as + # a required branch-protection status check — behaves identically to `required` on the + # publish side (same API calls); the distinction is purely how you configure GitHub. + # disabled — never create/update the check-run at all. RECOMMENDED for high-volume autonomous + # self-hosting: avoids GitHub showing "Expected — Waiting for status to be reported" + # under review-queue pressure, and reduces GitHub API calls (no check-run create/update). + # Before switching to `disabled`, remove "Gittensory Orb Review Agent" from this repo's + # branch-protection / ruleset required-status-checks list — Gittensory cannot do this on + # your behalf (it is a GitHub branch-protection setting), and leaving it required with + # nothing to satisfy it means GitHub will show a pending status forever. Keep your real + # CI/codecov/security checks required — this only ever affects Gittensory's own check. + # required | visible | disabled. Default: disabled for a never-configured repo (matches the existing + # opt-in default); an already-configured repo keeps its current effective behavior. RECOMMENDED for + # high-volume autonomous self-hosting: `visible` or `disabled`, not `required` — Gittensory's own + # merge/close decisions never depend on this check either way. + checkMode: visible + + # Policy pack. + # gittensor — confirmed-contributor-gated, registry-aware. + # oss-anti-slop — runs the deterministic rules against ANY author on ANY repo. + # Default: gittensor. + pack: gittensor + + # Linked-issue gate — what happens when a PR has NO linked issue at all. off | advisory | block. + # Default: advisory (a missing issue is surfaced in the review panel but never blocks the gate; issues + # aren't always available, e.g. small/self-evident fixes). Set `block` here, or turn on the dashboard + # "Require linked issue" toggle, to make a missing issue an explicit opt-in blocker. + # (If the dashboard toggle is on but this is still `off`, it is auto-promoted to `block`.) + # This is UNRELATED to closing a PR that links an INELIGIBLE issue (owner-assigned, wrong label, etc.) — + # that is a separate, deterministic rule, not this gate. + linkedIssue: advisory + + # Duplicate-PR gate — detects duplicate/superseding PRs. + # off | advisory | block. Default: block. + duplicates: block + + # Quality / merge-readiness score gate (the PR-quality score). + readiness: + # off | advisory. Default: advisory. Informational only — this dimension can + # never hard-block a PR, so "block" is not an accepted value (a config that + # still says "block" is downgraded to "advisory" with a warning). For an + # enforceable quality floor, use mergeReadiness or manifestPolicy below. + mode: advisory + # At/above this score the quality dimension passes. + # Number 0–100, or null for the engine default band. Default: null. + minScore: null + + # Deterministic anti-slop signal. Opt-in. + slop: + # off | advisory | block. Default: off. + # advisory — surfaces the slop score + warnings. + # block — also hard-blocks at/above minScore. + mode: off + # Slop-risk threshold at/above which `block` blocks. + # Number 0–100, or null. Default: null (engine uses 60, the "high" band). + minScore: null + # When true AND slop is not off, a free Workers-AI pass adds an + # advisory-only ai_slop_advisory finding. It never feeds the slop score or + # the gate. Bool. Default: false. + aiAdvisory: false + + # Oversized-PR gate. A PR at/above EITHER the file-count or line-count threshold + # (engine defaults, not configurable here) gets a manual-review HOLD finding — + # never a hard blocker; mode only turns this hold signal on or off. + # off | advisory | block. Default: off. Config-as-code only — no DB column or + # dashboard toggle; this can only be set here. + size: + mode: off + + # Lockfile-tamper-risk gate. Scans a changed package-lock.json diff for a + # resolved/integrity value that changed WITHOUT the same package's version + # changing in a changed package.json, or a resolved URL outside + # registry.npmjs.org — the classic supply-chain hand-edit tell. Distinct + # from the OSV.dev known-CVE dependency scan (a different threat model). + # off | advisory | block. Default: off. Config-as-code only — no DB column + # or dashboard toggle; this can only be set here. + lockfileIntegrity: off + + # CLA / license-compatibility gate (#2564). Confirms contributor license-agreement consent before a PR + # can auto-merge — the gittensory analog of a "CLA assistant" bot. off | advisory | block. Default: off. + # advisory — surfaces a cla_consent_missing finding but never blocks. + # block — also hard-blocks (one-shot close for a contributor) when neither detection method + # below confirms consent. + # Config-as-code only — no DB column or dashboard toggle; this can only be set here. + claMode: off + cla: + # A phrase gittensory looks for in the PR description (case-insensitive substring match), mirroring + # review.pre_merge_checks' descriptionContains. String or null. Default: null (not configured). + consentPhrase: "I have read and agree to the CLA" + # Name of a separate CLA-bot check-run this repo also runs (e.g. a CLA Assistant GitHub Action). A + # success/neutral conclusion for a check-run with this exact name also satisfies consent only when the + # run was produced by checkRunAppSlug. Either method configured is enough; both may be set. String or + # null. Default: null (not configured). + checkRunName: null + # Trusted GitHub App slug that must have produced checkRunName. Required for check-run detection so a + # contributor-controlled same-name check-run cannot satisfy a blocking CLA/legal gate. String or null. + # Default: null (not configured). + checkRunAppSlug: null + + # CI check/status context names to treat as REQUIRED when GitHub branch protection returns no + # readable required-status-checks (unconfigured, or a 403 from a token lacking + # administration:read — common for GitHub App installations, especially self-host). Merged with + # any branch-protection required contexts when both are readable; used ALONE when branch + # protection is null/empty. A context listed here that never appears on the commit stays + # pending; a completed red check for a listed context fails the gate; every listed context + # settled clean resolves to a verified "passed" (no completeness warning). List of strings, or + # omit. Default: not configured (keeps today's fold-all fail-closed behavior when branch + # protection is also unreadable). Config-as-code only — no DB column or dashboard toggle. + expectedCiContexts: + - build + - test + + # Composite merge-readiness gate (no min score). + # off | advisory | block. Default: off. + mergeReadiness: off + + # Manifest-policy gate. When `block`, this repo's declared policy from + # section 1 (blockedPaths, required linked issue, testExpectations) becomes + # an enforceable blocker. Independent of mergeReadiness. + # off | advisory | block. Default: off. + manifestPolicy: off + + # Self-authored-linked-issue gate — blocks (or advises on) a PR whose only + # linked issue was opened by the PR's own author, which the readiness/quality + # signals can't otherwise catch. off | advisory | block. Default: advisory. + # DB-backed (dashboard-settable too); this overrides the stored value. + selfAuthoredLinkedIssue: advisory + + # Gate-check dry-run. When true, the posted check conclusion remains the real + # non-enforcing verdict while comments/check text may also show the would-be + # stricter verdict for AI-review blocker mode. It does not disable downstream + # merge/close planning for failures from already-enforced gates. Bool. + # Default: false. Config-as-code only — no DB column or dashboard toggle; + # this can only be set here. + dryRun: false + + # First-time-contributor grace. RESERVED / currently INERT: this value is + # parsed and stored, but the gate does not read it — a first-time + # contributor with a real blocker is one-shot closed the same as a + # repeat contributor (blocker findings must remain closure outcomes). + # Setting this to true has no effect today; kept for potential future + # use. Bool. Default: false. + firstTimeContributorGrace: false + + # Live premerge migrations/** collision recheck (#2550, anti-abuse-adjacent safety net). When true, a PR + # touching migrations/** gets a fresh GitHub read of the base branch's CURRENT migration filenames + # immediately before an agent-driven merge (not just at CI time against this PR's own branch snapshot) — + # catching the case where a DIFFERENT PR merged a same-numbered migration file in the meantime. A live + # collision holds the PR (rebase-needed label + comment) instead of merging blind. Config-as-code only (no + # dashboard/DB equivalent). Bool. Default: false. Costs one extra GitHub API call per migrations/**-touching + # PR, so it is opt-in rather than a new default. + premergeContentRecheck: false + + # Force-rebase-before-merge window in minutes (#2552, anti-race). When the base branch has advanced within + # this many minutes of the actual merge-decision moment, an agent-driven merge forces an update_branch + + # fresh CI recheck cycle before merging, instead of trusting a mergeable_state: clean read that may already + # be stale relative to a sibling commit that just landed on the base. A bounded retry cap prevents a + # fast-moving base from live-locking the PR — after a few forced attempts it falls through to a normal + # merge with an audit note. Positive integer (minutes), or omit/null. Default: null (never force). + # DB-backed (dashboard-settable too); this overrides the stored value. + requireFreshRebaseWindow: 10 + + # AI maintainer review. Opt-in; the AI capabilities are switched on at the + # deployment level. + aiReview: + # off | advisory | block. Default: off. + # advisory — posts AI review notes only. + # block — a dual-model high-confidence consensus defect may become a + # blocker (confirmed-contributors only). + mode: off + # Use the maintainer's frontier model for the *advisory* write-up when a + # provider key is configured. The consensus blocker always uses the free + # Workers-AI pair, so BYOK never changes who can be blocked. + # Bool. Default: false. + byok: false + # Review every PR author when AI review is enabled. Keep false to spend + # model calls only on the engine's default eligible authors. + # Bool. Default: false. + allAuthors: false + # BYOK provider. anthropic | openai | null. Default: null (use the stored + # key's own provider). Must match the stored key's provider or BYOK is + # skipped (Workers-AI fallback). The key itself lives only in the encrypted + # key store — never in this file. + provider: null + # Model override for the BYOK advisory write-up (e.g. claude-3-5-sonnet-latest). + # String or null. Default: null (the key record's model, else a conservative + # per-provider default). + model: null + # Minimum calibrated AI-reviewer confidence (0-1) recorded for cache and + # calibration context. Under `mode: block`, consensus and split AI-review + # defects still block regardless of this floor. Number 0–1, or null. + # Default: null (engine uses 0.93). Config-as-code only — no DB column or + # dashboard toggle; this can only be set here. + closeConfidence: null + # Per-repo override of the self-host operator's dual-AI combine strategy (#2567). + # single | consensus | synthesis, or null. Default: null (the operator's own + # AI_REVIEW_PLAN.combine env default, itself "consensus" if unset). A refinement + # only -- not floor-clamped like onMerge below, since the three strategies aren't + # ordered by strictness. + combine: null + # Per-repo override of the synthesis merge mode (#2567): "either" is STRICTER (any + # one reviewer's blocker blocks/holds); "both" is more permissive (every reviewer + # must agree). either | both, or null. Default: null (the operator's own plan). A + # repo may only TIGHTEN the operator's floor -- it can never loosen "either" down + # to "both". + onMerge: null + # Per-repo override of the named reviewer pair(s) to run, in place of the + # operator's own AI_REVIEW_PLAN.reviewers (or the free Workers-AI pair when the + # operator configured none). List of {model, fallback?}, or null. Default: null + # (the operator's plan). No operator floor applies to which reviewers run. + # reviewers: + # - model: claude-3-5-sonnet-latest + # fallback: claude-3-haiku-latest + + +# ---------------------------------------------------------------------------- +# 3. GENERIC SETTINGS (`settings:`) — dashboard-equivalent overrides +# ---------------------------------------------------------------------------- +# Everything a maintainer can toggle in the dashboard can be set here as code. +# All values shown are the safe defaults; delete any line to inherit it. +# +# Review output controls. These tune review output without changing the +# deterministic gate policy above. Omit the block to keep the byte-identical +# defaults. +review: + # Deterministic AI review eligibility filters (`review.auto_review`, #1954 / #2038–#2041). Each knob quietly + # skips the advisory AI review for matching PRs — never a gate failure. When the Orb review check is enabled, + # skipped PRs complete as "skipped" with a human-readable reason (see `evaluateAutoReviewSkipReason` in + # `src/signals/focus-manifest.ts` and the public-surface `SKIP_SUMMARY.ignored_author` mapping in + # `src/signals/settings-preview.ts`). + # + # Invariant: every field below defaults to off/empty/null, which is byte-identical to today's behavior — every + # PR is reviewed exactly as before. Skips are quiet and never block merge on their own. + auto_review: + # Bool | null. Default: null (draft PRs are reviewed as today). When true, draft PRs skip AI review. + # skip_drafts: true + + # Glob list of PR author logins whose PRs skip AI review. Useful for dependency bump or release automation + # that already has separate policy/CI. `*` and `**` match any run; matching is case-insensitive. + # Default: [] (every author remains review-eligible). + ignore_authors: + - "*[bot]" + - dependabot + - renovate + + # Case-insensitive title substrings that skip AI review (e.g. WIP/DRAFT markers). Default: []. + # ignore_title_keywords: + # - WIP + # - DRAFT + + # Base-ref globs whose PRs ARE reviewed. When non-empty, PRs targeting other bases skip review. + # Default: [] (every base branch is in scope). + # base_branches: + # - main + # - release/** + + # Non-negative integer. After N published AI reviews on this PR, pause further re-reviews. + # Default: null / 0 (re-review every sync, byte-identical). + # auto_pause_after_reviewed_commits: 3 + + # Planned eligibility knobs (tracked separately — not parsed yet; omit until shipped): + # skip_labels, skip_docs_only, max_added_lines, max_files (#2062–#2065). + + # Planned display toggle (#2069): when enabled, the unified review comment includes a + # `review effort: N/5 (~M min)` line derived from the per-PR effort estimate. + # effort_score: false + + # Deterministic label suggestions (#2045). Each rule SUGGESTS a non-scoring label when a PR matches ALL of the + # `when` criteria it sets (at least one is required): when_paths (any changed path matches a glob), title_contains, + # description_contains (both case-insensitive). Suggestions are advisory; they are auto-applied only when the repo's + # autoLabelEnabled is on. Reserved `gittensor:` labels (scoring/type) are refused. Empty/unset ⇒ no suggestions. + labeling_rules: + - label: area:docs + when_paths: + - "docs/**" + - "**/*.md" + - label: needs:migration + when_paths: + - "migrations/**" + description_contains: schema + +settings: + # Who receives the public PR comment. + # off | detected_contributors_only | all_prs. Default: detected_contributors_only. + commentMode: detected_contributors_only + + # Public audience framing. oss_maintainer | gittensor_only. Default: oss_maintainer. + publicAudienceMode: oss_maintainer + + # How much signal the public surface shows. minimal | standard. Default: standard. + publicSignalLevel: standard + + # Publish a GitHub check-run. off | enabled. Default: off. + # Independent, legacy advisory surface (#2691) -- separate from, and not gated + # by, the required `Gittensory Orb Review Agent` gate check under `gate:` + # above. At the default `checkRunDetailLevel: minimal` below it posts no + # findings at all; at standard/deep it only re-renders findings already shown + # in the gate check and the PR comment. One-shot self-host installs should + # leave this at the default `off` unless you specifically want a second, + # low-detail check-run surface alongside the required gate check. + checkRunMode: off + + # Check-run detail. minimal | standard | deep. Default: minimal. + checkRunDetailLevel: minimal + + # Which public surfaces are used. + # off | comment_and_label | comment_only | label_only. Default: comment_and_label. + publicSurface: comment_and_label + + # Apply the gittensor label automatically. Bool. Default: true. + autoLabelEnabled: true + + # The label name to apply. String. Default: gittensor. + gittensorLabel: gittensor + + # Apply the per-PR TYPE/taxonomy label -- bug/feature by the PR title, or priority via + # `linkedIssueLabelPropagation` below (#label-decoupling). Independent of `autoLabelEnabled` above + # (which only governs the base `gittensorLabel` context label): a type label is internal triage + # metadata, so it applies to every PR regardless of author type (bot/maintainer/missing author) or + # `publicAudienceMode`/`publicSurface`, UNLESS `publicAudienceMode: gittensor_only` is muting this + # PR's author entirely (that mode's whole point is total silence for a non-confirmed-miner author). + # Bool. Default: true. + typeLabelsEnabled: true + + # Per-repo TYPE label config: an open `category -> label name` map, not fixed to three names. + # `bug`/`feature`/`priority` are the built-in categories `deriveKindFromTitle` and + # `linkedIssueLabelPropagation` classify (default gittensor:bug / gittensor:feature / + # gittensor:priority, shown below as an EXAMPLE, not an engine requirement) -- omit any of the three + # to keep its default. Add any number of your OWN categories beyond them (they are never chosen by + # title-classification, only ever by a `linkedIssueLabelPropagation` mapping below, but registering + # the name here makes it participate in the same mutual-exclusivity cleanup). An explicit `{}` means + # zero configured categories for this repo -- distinct from omitting `typeLabels` entirely, which + # inherits whatever the dashboard/API has stored. + # typeLabels: + # bug: gittensor:bug + # feature: gittensor:feature + # priority: gittensor:priority + # security: area:security + # docs: area:docs + # dependency: area:dependency + # custom: maintainer:custom + + # Linked-issue label propagation (#priority-linked-issue-gate): the ONLY mechanism that can select a + # label like `gittensor:priority` -- it is NEVER inferred from a PR's title, changed files, AI + # output, or existing PR labels, only ever copied from a linked/closing issue ("Fixes #123") that + # ALREADY carries the configured issue label. Generic beyond the priority use case: any issue label + # can map to any PR label. `removeOtherTypeLabels: true` REPLACES the type label entirely (bug/ + # feature are removed), matching how `gittensor:priority` behaves today; `false` applies the mapped + # label ADDITIVELY alongside the normal title-based bug/feature label, leaving it untouched -- useful + # for a mapping unrelated to the bug/feature/priority triad (e.g. a `customer:vip` issue label + # copied to a `triage:vip` PR label). Disabled by default (no mappings) -- a self-hoster opts in per + # repo. If your labels carry reward or moderation weight, configure this in PRIVATE per-repo/global + # config (see `config/examples/README.md`) rather than the public `.gittensory.yml`, so contributors + # cannot see the exact mapping rules. A per-repo `mappings` override in the private-config layer + # REPLACES the global default list wholesale -- it does not merge with it. + # linkedIssueLabelPropagation: + # enabled: true + # mode: exclusive_type_label + # mappings: + # - issueLabel: gittensor:priority + # prLabel: gittensor:priority + # removeOtherTypeLabels: true + + # Create the label if it does not yet exist. Bool. Default: true. + createMissingLabel: true + + # Auto-assign a merged/linked-issue PR to a matching GitHub Project/Milestone (#3186). off | suggest | + # auto. Default: off. "suggest" posts an advisory note only; "auto" applies the match directly. + # autoProjectMilestoneMatch: off + # Which backend the match runs against. github | linear. Default: github. "linear" matches against a + # Linear workspace via a per-repo encrypted API key (set via the dashboard, never here). + # autoProjectMilestoneMatchBackend: github + + # Also review PRs authored by maintainers (not just detected contributors). + # Bool. Default: false. + includeMaintainerAuthors: false + + # Standing rule: the repo OWNER's own PRs (and ADMIN_GITHUB_LOGINS fleet-operator PRs, #2133) are NEVER + # auto-closed by default -- they may still auto-merge when clean + passing, or fall to a manual hold. + # Set true to make owner/admin-authored PRs eligible for auto-close like a contributor's (still gated by + # the `close` autonomy class below + adverse-signal conditions). Automation-bot PRs stay exempt regardless + # of this setting. Bool. Default: false. + closeOwnerAuthors: false + + # Hard manual-review path guardrails are config-as-code only. Omit or use [] for no path guardrails; + # set concrete globs when otherwise-mergeable PRs touching those paths must be held for a person. + # This never falls back to hidden engine path defaults. + hardGuardrailGlobs: [] + + # Require a linked issue on every PR (dashboard equivalent of the toggle that + # can auto-promote gate.linkedIssue to block). Bool. Default: false. + requireLinkedIssue: false + + # Backfill review of pre-existing open PRs when first installed. + # Bool. Default: true. + backfillEnabled: true + + # Use private trust signals in scoring. Bool. Default: true. + privateTrustEnabled: true + + # Render a README status badge for the repo. Bool. Default: false. + badgeEnabled: false + + # Per-repo kill-switch: when true, the agent does nothing on this repo. + # Bool. Default: false. + agentPaused: false + + # Dry-run / shadow mode: compute everything but take no public action. + # Bool. Default: false. + agentDryRun: false + + # Four independent label families, none of which gates or silently disables another (#label-decoupling, + # #label-scoping): + # 1. Context label (`gittensorLabel`, gated by `autoLabelEnabled` above) — the base per-PR marker shown + # to the public surface, subject to `publicAudienceMode`/`publicSurface`/author-qualification. + # 2. Taxonomy/type labels (`typeLabels` above, gated by `typeLabelsEnabled`) — internal triage + # metadata, applied independently of #1. Shown above with the gittensor:* default names, but + # the category set itself is config, not fixed to three (#label-modularity): bug/feature come + # from the PR title; priority (or any other category you register) ONLY from + # `linkedIssueLabelPropagation` above — never inferred from title, files, AI, or existing PR + # labels (#priority-linked-issue-gate). + # 3. Autonomy-outcome labels (`gittensory:ready-to-merge` etc.) — gated by the `review_state_label` + # autonomy class below, advisory commentary on the bot's own verdict. + # 4. Anti-abuse enforcement labels (blacklist/contributor-cap/review-nag) — gated by the `close` + # autonomy class below, applied alongside the close action they accompany. + # + # Autonomy dial — per-action-class level (observe … auto). Classes: review, request_changes, approve, + # merge, close, label, review_state_label, update_branch. Map. Default: {} (= observe everywhere, + # deny-by-default). + # + # `label` vs `review_state_label` (#label-scoping): these are SEPARATE, independent dials. + # - `label` alone no longer applies any planner-driven label by itself. The anti-abuse enforcement + # labels (blacklist/contributor-cap/review-nag) ride on the SAME dial as their accompanying close + # (`close`, below) — set `close: auto` and they close-and-label together with no `label` grant needed. + # - `review_state_label` gates the bot's own disposition-communication labels only: + # gittensory:ready-to-merge / gittensory:changes-requested / gittensory:needs-human-review / + # gittensory:migration-collision. These are advisory signals about the bot's own verdict, not + # enforcement — for a one-shot review model (merge/close/hold through the required gate check, no + # back-and-forth) leave this at the default `observe` so they never appear; set it to `auto` only if + # you specifically want that running commentary as GitHub labels. + autonomy: {} + + # Names for the 4 disposition-outcome labels the `review_state_label` autonomy class above applies. + # Each is a plain string, or explicit `null` to disable that ONE label without disabling the underlying + # merge/close/hold decision it accompanies. + # readyToMergeLabel: ready-to-merge # Default: ready-to-merge. + # changesRequestedLabel: changes-requested # Default: changes-requested. + # manualReviewLabel: manual-review # Default: manual-review. Also the fallback hold label used + # # when review_state_label is off but merge/close is acting + # # (guardrail holds, migration collisions, etc.). + # migrationCollisionLabel: migration-collision # Default: migration-collision. + + # Auto-maintain policy for merges the agent is allowed to perform. + # Defaults: mergeMethod = squash, requireApprovals = 1. + autoMaintain: + mergeMethod: squash + requireApprovals: 1 + + # Command authorization role policy — which roles (maintainer | collaborator | pr_author | + # confirmed_miner) may invoke each bot command. Map. Default: the built-in policy. + # Omit to inherit the DB-stored policy, or the built-in default if neither is set. An + # invalid top-level shape (not a mapping) is ignored with a warning rather than + # overwriting an existing DB-stored policy with the built-in default. + # commandAuthorization: + # default: [maintainer, collaborator, confirmed_miner] + # commands: + # gate-override: [maintainer, collaborator] + + # Config-driven contributor ban list (#1425, anti-abuse): a banned login's PR/issue is closed + # BEFORE any merit/CI/AI analysis (deterministic, zero-hallucination). `reason`/`evidence`/`addedAt` + # are maintainer-only metadata, never published in the automated close comment. Default: [] (no bans). + # contributorBlacklist: + # - login: some-banned-login + # reason: "plagiarized PR content" + # evidence: ["https://github.com/owner/repo/pull/123"] + # addedAt: "2026-01-01" + + # Label applied alongside a blacklist close above. String. Gated on `autonomy.close` (#label-scoping); + # set to explicit `null` to close without any label. Default: slop. + # blacklistLabel: slop + + # Per-contributor open-PR/open-issue caps (#2270, anti-abuse): the max PRs/issues a single + # non-owner/non-admin/non-bot contributor may have open on this repo at once. Uncomment and set a + # number to opt in — a contributor's newest item above the cap is closed with a clear reason on the + # PR-opened/issue-opened webhook; their oldest items up to the cap stay open and reviewable. + # Both contributorOpenPrCap and contributorOpenIssueCap ARE ENFORCED. Positive whole number, or + # omit/null for no cap. Default: null (disabled) for both. Set explicitly to `null` (not just omitted) + # to force-clear a cap that a dashboard/DB write previously set. + # contributorOpenPrCap: 2 + # contributorOpenIssueCap: 5 + + # Label applied to a PR/issue closed for exceeding a cap above. String. Default: over-contributor-limit. + # Gated on `autonomy.close` (#label-scoping), not `autonomy.label` — closing WITHOUT this label is + # available too: set this to explicit `null` (not just omitted) to close silently, with no label. + # contributorCapLabel: over-contributor-limit + + # Cancel in-flight CI runs when a PR is auto-closed for exceeding contributorOpenPrCap above (#2462). + # Requires the App installation to have granted the `actions: write` permission -- degrades gracefully + # (skipped + logged, the close itself still succeeds) when it hasn't. Bool or omit/null to fall back to + # the CONTRIBUTOR_CAP_CANCEL_CI_DEFAULT env var. Default: null (falls back to the env var, itself off + # by default). + # contributorCapCancelCi: true + + # Review-request nagging cooldown (#2463, anti-abuse): throttle a non-owner/non-admin/non-bot + # contributor who repeatedly pings @gittensory for review on the same PR/issue. "hold" replies with a + # cooldown notice and takes no further action; "close" closes the PR (issues degrade to "hold" until + # a dedicated closeIssue primitive lands) with a clear reason. Off by default. + # reviewNagPolicy: off # off | hold | close. Default: off. + # reviewNagMaxPings: 3 # Positive integer. Pings above this within the cooldown window trigger the policy. Default: 3. + # reviewNagCooldownDays: 5 # Positive integer up to 365. Window the ping count is measured over. Default: 5. + # reviewNagLabel: review-nag-cooldown # Label applied alongside the hold/close action. Default: review-nag-cooldown. + # # Gated on autonomy.close (#label-scoping); set to explicit `null` to + # # close/hold without any label. + + # Maintainer-mention nag moderation (#label-scoping): GitHub logins ALSO throttled under the SAME + # reviewNagPolicy/reviewNagMaxPings/reviewNagCooldownDays/reviewNagLabel cooldown above, on top of the + # bot's own @gittensory handle — e.g. a contributor who keeps tagging a specific maintainer for review + # instead of (or in addition to) pinging @gittensory. Counted independently per mentioned login and + # independently of the @gittensory counter. Only fires for the thread's OWN author, never a third party + # commenting on someone else's thread; owner/admin/automation-bot/autoCloseExemptLogins are always exempt. + # List of GitHub logins. Default: [] (no logins watched). + # reviewNagMonitoredMentions: [your-maintainer-login] + + # Shared repo-scoped exemption list (#2463): GitHub logins never throttled/closed by gittensory's + # deterministic anti-abuse mechanisms (review-nag cooldown today; the per-contributor open-item cap + # above will reuse this list too), on top of the standing owner/admin/automation-bot exemption. + # List of GitHub logins. Default: [] (no additional exemptions). + # autoCloseExemptLogins: [some-trusted-regular] + + # Account-age throttle (#2561, anti-abuse): a PR from an account younger than this many days gets + # the newAccountLabel below -- friction/visibility only, NEVER an automatic close on account age alone. + # Never fires for the repo owner, admin logins, or automation bots. PR-path only. Off by default. + # accountAgeThresholdDays: null # Positive integer, or null/omitted to disable. Default: null (off). + # newAccountLabel: new-account # Label applied to a below-threshold-age account's PR. Default: new-account. + + # Per-command @gittensory rate limit (#2560, anti-abuse): generalizes review-nag's cooldown pattern to + # EVERY @gittensory command (help/ask/preflight/blockers/... and the maintainer queue-digest commands), + # not just review-request pings — independent of review-nag's own thread-author-only scope. "hold" + # replies with a cooldown notice and skips the command's own dispatch. Off by default. + # commandRateLimitPolicy: off # off | hold. Default: off. + # commandRateLimitMaxPerWindow: 20 # Positive integer. Per-command invocation limit for a CHEAP command (cache-only, no AI call) within the window. Default: 20. + # commandRateLimitAiMaxPerWindow: 5 # Positive integer. Tighter limit for an AI-cost-bearing command (ask/blockers/preflight/reviewability/packet/duplicate-check/next-action/repo-fit). Default: 5. + # commandRateLimitWindowHours: 24 # Positive integer. Rolling window (hours) both limits above count against. Default: 24. + + # Moderation-rules engine (#selfhost-mod-engine): a single shared, cross-repo violation tally across the + # anti-abuse mechanisms below that already short-circuit a PR/issue's disposition (contributor cap, + # blacklist, review-nag, review-evasion). Off by default; the global config (dashboard/API, not this file) + # holds the master switch, threshold, and label text. This per-repo override only opts THIS repo in/out and + # narrows which mechanisms feed the tally for it. + # moderationGateMode: inherit # inherit | off | enabled. Default: inherit (defer to the global master switch). + # moderationRules: [contributor_cap, blacklist, review_nag, review_evasion] # Replaces (not unions with) the global rule set for this repo. Default: inherit the global list. + # moderationWarningLabel: mod:warning # Label applied at >=1 lifetime violation. Default: the global config's warningLabel. + # moderationBannedLabel: mod:banned # Label applied at >= the ban threshold. Default: the global config's bannedLabel. + + # Review-evasion protection (#review-evasion-protection, anti-abuse): a contributor closing or converting + # their own PR to draft while gittensory has an ACTIVE review pass running against it is dodging the + # one-shot review process, not making an ordinary close. When enabled, gittensory reopens (if needed) and + # re-closes the PR as the App -- a close the contributor cannot themselves reopen (#one-shot-reopen) -- + # posts an explanation comment, applies the configured label, and records a `review_evasion` moderation + # strike (subject to moderationRules above including it). Off by default. + # reviewEvasionProtection: off # off | close. Default: off. + # reviewEvasionLabel: review-evasion # Label applied alongside the enforcement close. Gated on autonomy.close + # # (#label-scoping); set to explicit `null` to close without any label. + # # Default: review-evasion. + # reviewEvasionComment: true # Post the public explanation comment before the enforcement close. Default: true. + + # Linked-issue HARD-RULE auto-close (#linked-issue-hard-rules): a DETERMINISTIC verdict about the + # linked issue itself (not an AI verdict), gated per-rule to block|off. Fires regardless of + # `guardrailHit`; still respects the `close` autonomy class and the owner/automation exemption. Off + # by default for every rule. + # linkedIssueHardRules: + # ownerAssignedClose: off # Close when the linked issue is assigned to the repo owner. block | off. + # assignedIssueClose: off # Close when it's assigned to someone other than the PR author. block | off. + # missingPointLabelClose: off # Close when a default-label repo's issue carries none of pointBearingLabels. block | off. + # maintainerOnlyLabelClose: off # Close when the issue carries a maintainerOnlyLabels entry. block | off. + # pointBearingLabels: [gittensor:bug, gittensor:feature, gittensor:priority] + # maintainerOnlyLabels: [maintainer-only] + # defaultLabelRepo: false # Whether this repo's default label set makes missingPointLabelClose meaningful. + # verifyBeforeClose: true # Two-pass flag-then-close instead of an immediate close (#linked-issue-verify-before-close). + # closeDelaySeconds: 30 # Verification window before Pass 2 closes, when verifyBeforeClose is true. + # Label applied by Pass 1 of the flag-then-close verification above (present ⇒ Pass 2 may close on the + # next check). Gated on `autonomy.review_state_label`. String. Default: pending-closure. + # pendingClosureLabel: pending-closure + + # Unlinked-issue-match guardrail (#unlinked-issue-guardrail): when a contributor PR links no issue, a + # deterministic pre-filter + AI verification checks whether it silently solves an existing open issue + # anyway (a way to dodge linked-issue scrutiny while still farming merge-ratio credibility upstream). + # A first confirmed match HOLDS the PR for manual review; a confirmed REPEAT by the same contributor + # escalates to a close. Off by default. + # unlinkedIssueGuardrail: + # mode: off # off | hold. Default: off. + # minConfidence: 0.85 # Number 0-1. Minimum AI-verifier confidence to treat a candidate as a real match. + +# Maintainer AI review tuning (`.gittensory.yml` top-level `review:` block). These knobs shape the advisory AI +# review prompt and file selection only — gate/slop/secret-scan are unaffected. +# review: +# # Globs whose matching files are dropped from the AI review (lockfiles, generated output, etc.). +# # Applied before path_filters. Empty/default ⇒ every file is reviewed. +# exclude_paths: +# - "**/*.lock" +# - "dist/**" +# # Include + `!`-negation globs that positively scope the AI review AFTER exclude_paths. +# # Plain entries restrict to matching paths; a leading `!` subtracts matches. Both `*` and `**` cross +# # directory slashes. Empty/default ⇒ every non-excluded file is reviewed (byte-identical). +# path_filters: +# - "src/**" +# - "!src/generated/**" +# # Public-safe voice brief complementing review.profile (e.g. concise, cite line numbers). null/unset ⇒ byte-identical prompt. +# tone: "Be concise and cite line numbers." +# # How nitpicky the AI maintainer review is. chill | balanced | assertive. Default: balanced (absent). +# # chill = only blocking defects; assertive = also minor nits. Never changes the gate verdict. +# profile: balanced +# # When true, the reviewer prioritizes a security-defect category with elevated scrutiny, on top of +# # whatever `profile` volume is set. Bool or null. Default: null/false (byte-identical prompt). +# security_focus: false +# # A repo-level natural-language brief handed to the AI reviewer on EVERY review (vs the per-path +# # path_instructions below) -- the maintainer's conventions/voice. Bounded + public-safe at parse time. +# # String or null. Default: null (byte-identical prompt). +# instructions: "Prefer small, focused PRs. Flag any missing test for a bug fix." +# # Per-path natural-language guidance handed to the AI reviewer when a changed file matches the glob. +# # Empty/default ⇒ byte-identical prompt. +# path_instructions: +# - path: "src/db/**" +# instructions: "Flag any migration missing a matching down-path note." +# # When true, the AI reviewer ALSO leaves quiet, non-blocking inline PR comments on specific changed +# # lines, in addition to the decision summary. Bool or null. Default: null/false (no inline comments). +# # Operator-gated too (GITTENSORY_REVIEW_INLINE_COMMENTS + allowlist). +# inline_comments: false +# # When true, an inline finding whose fix is precise enough to anchor to one line is ALSO rendered as +# # a one-click GitHub suggestion block. Only takes effect when inline_comments is already on. Bool or +# # null. Default: null/false. +# suggestions: false +# # When true, the unified review comment (only rendered when the unifiedComment feature is on) gains a +# # deterministic "Changed files" summary: one row per file category, with counts and +/- totals. Bool +# # or null. Default: null/false. +# changed_files_summary: false +# # When true, an inline finding is ALSO tagged with a category (security/correctness/performance/ +# # maintainability/tests/style) -- the AI reviewer self-categorizes, with a deterministic path/keyword +# # fallback for whatever it omits. Only takes effect when inline_comments is already on. Bool or null. +# # Default: null/false. +# finding_categories: false +# # Maintainer-declared DETERMINISTIC content assertions (title/description must contain a phrase, a +# # label must be present), optionally gated to a path glob. A failed check is advisory by default; +# # `enforce: true` makes it a hard gate blocker. Empty/default ⇒ no finding (no AI judgment involved). +# pre_merge_checks: +# - name: "require a linked issue reference in the description" +# description_contains: "Fixes #" +# enforce: false +# # Per-repo REES enrichment-analyzer toggles (analyzer name -> on/off). Unknown keys warn + drop at +# # parse. Empty/default ⇒ the operator's default analyzer set runs unchanged. +# enrichment: +# deep-nesting: true +# error-swallow: false +# # Deterministic {label, when} rules that SUGGEST a non-scoring label when a PR's changed paths/ +# # title/description match ALL of the rule's `when` criteria. Auto-applied only when the repo's +# # autoLabelEnabled is set. Reserved gittensor:* labels are refused at parse. Empty ⇒ no suggestion. +# # (This block is also active, uncommented, near the top of the live `review:` section above.) +# labeling_rules: +# - label: area:docs +# when_paths: ["docs/**", "**/*.md"] +# # Per-repo self-host reviewer model/effort overrides (claude-code / codex). Self-host only; a hosted +# # (Workers-AI) repo ignores this entirely. All-null/default ⇒ the operator's global env vars apply. +# ai_model: +# claude_model: null # Overrides CLAUDE_AI_MODEL for this repo. String or null. +# claude_effort: null # Overrides CLAUDE_AI_EFFORT for this repo. String or null. Default (env unset): medium. +# codex_model: null # Overrides CODEX_AI_MODEL for this repo. String or null. +# codex_effort: null # Overrides CODEX_AI_EFFORT for this repo. String or null. Default (env unset): medium. +# # Per-repo before/after screenshot-capture config (#3609 preview / #3610 routes). Only takes effect when +# # the operator has ALSO enabled GITTENSORY_REVIEW_SCREENSHOTS + this repo's cutover allowlist -- this +# # config narrows/redirects that feature, it never turns it on by itself. All-null/empty/default ⇒ +# # byte-identical to today (GitHub-native preview discovery, automatic file-to-route inference). +# visual: +# preview: +# # The repo's "after" preview URL, with {number}/{head_sha}/{head_sha_short} placeholders substituted +# # at capture time. ALWAYS wins over GitHub-native preview discovery (Deployments API / commit checks / +# # cloudflare-bot PR comment) when set -- the only option for a provider (e.g. Cloudflare Workers +# # Builds' non-production branch builds) that never surfaces a GitHub-visible deployment at all. Must +# # resolve to a valid HTTPS URL targeting a public host. String or null. Default: null (discovery unchanged). +# url_template: "https://pr-{number}.myapp.workers.dev" +# routes: +# # An explicit, always-screenshotted route list. When non-empty, REPLACES automatic file-to-route +# # inference entirely -- for a repo whose routing convention isn't gittensory-ui's TanStack file-based +# # one. Empty/default ⇒ automatic inference (falling back to "/" when nothing matches). +# paths: +# - "/pricing" +# - "/docs" +# # Overrides the built-in cap (2) on how many routes get screenshotted per PR, whether they come from +# # `paths` above or automatic inference. Positive integer or null. Default: null (built-in default). +# max_routes: 3 +# # Maintainer overrides for the public review-panel CONTENT (not what gittensory measures). The +# # Gittensor attribution + register link is always appended to the footer regardless; maintainer text +# # failing the public-safe filter is dropped, never published. +# footer: +# text: "Reviewed by the Acme maintainer bot." # Custom lead line. String or null. Default: null. +# note: "Run the test suite before requesting review." # Short intro line shown above the panel. String or null. +# # Per-row show/hide toggles for the panel. Keys: linkedIssue | relatedWork | reviewLoad | +# # validationEvidence | openPrQueue | contributorContext | gateResult. Default: all shown (true). +# fields: +# relatedWork: false +# openPrQueue: false +# # See the active `review.auto_review` block above for the full eligibility reference (defaults, types, examples). +# # The commented snapshot below mirrors a typical self-host setup: +# auto_review: +# skip_drafts: true +# ignore_authors: +# - "*[bot]" +# ignore_title_keywords: +# - WIP +# - DRAFT +# base_branches: +# - main +# - release/** +# auto_pause_after_reviewed_commits: 3 + +# Per-repo activation overrides for the converged review features that ship behind a deployment-wide +# GITTENSORY_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety). Each key is `true` (force on +# for this repo, subject to the env flag still being enabled), `false` (force off), or omitted (falls back +# to the GITTENSORY_REVIEW_REPOS allowlist default -- an operator who sets nothing keeps today's behavior). +# features: +# rag: true +# reputation: false +# unifiedComment: true +# safety: true + +# Registry-review lane (#2435): lets a self-hosted maintainer point gittensory at their OWN structured +# registry (e.g. a subnet/plugin/package catalog) without a gittensory code change -- reviewing additions +# to a data file the same way it reviews code. Uncomment and set at least entryFileGlob + collectionField +# (both required; the block is ignored with a warning if either is missing). +# contentLane: +# entryFileGlob: registry/*.json # Glob for the structured entry files this lane reviews. Required. +# collectionField: entries # The JSON field holding the collection this lane diffs. Required. +# providerFileGlob: providers/*.ts # Optional glob for source files the entries are validated against. +# artifactGlob: dist/registry.json # Optional glob for a generated/build artifact to cross-check. +# maxAppendedEntries: 1 # Positive integer cap on new entries per PR. Default: unbounded. +# duplicateKeyFields: [slug] # Field name(s) used to detect a duplicate entry. Default: [] (no dedup check). +# validatorId: my-registry-validator # Optional identifier for a custom per-entry validator. Default: none. + +# Repo-doc generation (#2993/#3002): opt-in only, off by default. Uncomment to let Gittensory open a PR +# generating/refreshing AGENTS.md/CLAUDE.md (and later, skill files) from this repo's own profile. There +# is no DB-backed dashboard counterpart -- precedence is simply this manifest value, or fully disabled +# when the block (or `enabled`) is absent. +# repoDocGeneration: +# enabled: true # Bool. Default: false (never touched by the generator). +# scope: [agents] # agents | skills, one or more. Default: [] ("skills" is not shipped yet). +# allowOverwriteExisting: false # Bool. Opt-in to propose overwriting a hand-maintained, non-generated +# # AGENTS.md/CLAUDE.md. Default: false (left alone if not recognizably generated). +# refreshIntervalDays: 7 # Positive integer. Minimum days between scheduled refresh attempts. Default: 7. diff --git a/config/examples/gittensory.minimal.yml b/config/examples/gittensory.minimal.yml new file mode 100644 index 0000000000..ee3f557056 --- /dev/null +++ b/config/examples/gittensory.minimal.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# gittensory.minimal.yml — smallest safe starter config (#1682) +# ============================================================================ +# +# WHERE TO COPY (pick one): +# PUBLIC REPO — repo root as `.gittensory.yml` (or `.github/gittensory.yml`). Contributors can read it. +# PRIVATE SELF-HOST — `${GITTENSORY_REPO_CONFIG_DIR}/.gittensory.yml` (global default) or +# `${GITTENSORY_REPO_CONFIG_DIR}/owner__repo/.gittensory.yml` (per-repo override). Never commit real +# policy here to a public repo — use the private mount for thresholds, allowlists, and autonomy. +# +# Canonical copy also lives at the repo root as `.gittensory.minimal.yml` (kept in sync by CI). +# For every supported field, defaults, and allowed values see `gittensory.full.yml` or +# `.gittensory.yml.example`. See `TEMPLATES.md` in this directory for fleet examples. +# +# Safe by default: +# - Gate off (enable explicitly when you are ready) +# - Autonomy observe-only (deny-by-default — no auto-merge, auto-close, or auto-label) +# - No auto-maintain / write behavior unless you add it later +# ============================================================================ + +# Optional: declare work areas you want contributors to focus on. +# wantedPaths: +# - "src/**" + +gate: + enabled: false + +settings: + autonomy: + merge: observe + close: observe + label: observe + review_state_label: observe diff --git a/test/unit/config-templates.test.ts b/test/unit/config-templates.test.ts new file mode 100644 index 0000000000..2325ac2178 --- /dev/null +++ b/test/unit/config-templates.test.ts @@ -0,0 +1,62 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; +import { isAgentConfigured } from "../../src/settings/autonomy"; +import { + gateConfigToJson, + parseFocusManifest, + parseFocusManifestContent, +} from "../../src/signals/focus-manifest"; + +// #1682: self-host operators need discoverable, copy-paste templates under config/examples/ that +// parse cleanly, stay in sync with the canonical root files, and keep the minimal starter safe. + +const CANONICAL_BODY_MARKER = "# WHERE IT LIVES (first match wins):"; +const MINIMAL_BODY_MARKER = "# Safe by default:"; + +function readConfigExample(name: string): string { + return readFileSync(`config/examples/${name}`, "utf8"); +} + +function readRoot(name: string): string { + return readFileSync(name, "utf8"); +} + +function bodyFromMarker(content: string, marker: string): string { + const index = content.indexOf(marker); + expect(index, `marker ${JSON.stringify(marker)} missing`).toBeGreaterThanOrEqual(0); + return content.slice(index); +} + +describe("config/examples review templates (#1682)", () => { + it("gittensory.full.yml body matches .gittensory.yml.example from WHERE IT LIVES onward", () => { + const full = readConfigExample("gittensory.full.yml"); + const example = readRoot(".gittensory.yml.example"); + expect(bodyFromMarker(full, CANONICAL_BODY_MARKER)).toBe(bodyFromMarker(example, CANONICAL_BODY_MARKER)); + }); + + it("gittensory.minimal.yml body matches .gittensory.minimal.yml from Safe by default onward", () => { + const minimal = readConfigExample("gittensory.minimal.yml"); + const root = readRoot(".gittensory.minimal.yml"); + expect(bodyFromMarker(minimal, MINIMAL_BODY_MARKER)).toBe(bodyFromMarker(root, MINIMAL_BODY_MARKER)); + }); + + it("parses gittensory.full.yml with zero warnings", () => { + const manifest = parseFocusManifestContent(readConfigExample("gittensory.full.yml"), "repo_file"); + expect(manifest.warnings).toEqual([]); + expect(manifest.present).toBe(true); + expect(manifest.gate.sizeMode).toBe("off"); + expect(manifest.features.rag).toBeNull(); + }); + + it("parses gittensory.minimal.yml with zero warnings and enables no agent actions", () => { + const manifest = parseFocusManifestContent(readConfigExample("gittensory.minimal.yml"), "repo_file"); + expect(manifest.warnings).toEqual([]); + expect(manifest.present).toBe(true); + expect(manifest.gate.enabled).toBe(false); + expect(isAgentConfigured(manifest.settings.autonomy)).toBe(false); + const round = parseFocusManifest({ gate: gateConfigToJson(manifest.gate), settings: { autonomy: manifest.settings.autonomy } }); + expect(round.warnings).toEqual([]); + expect(round.gate.enabled).toBe(false); + expect(isAgentConfigured(round.settings.autonomy)).toBe(false); + }); +});