Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .loopover.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -336,15 +336,16 @@ gate:
# 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).
# blocker.
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.
# Review every PR author when AI review is enabled. Only has an effect
# when `settings.aiReviewConfirmedContributorsOnly` (below) is also true --
# otherwise every author is already reviewed by default.
# Bool. Default: false.
allAuthors: false
# BYOK provider. anthropic | openai | null. Default: null (use the stored
Expand Down Expand Up @@ -680,6 +681,13 @@ settings:
# the safe-by-default add-only behavior. Bool. Default: false.
hardGuardrailGlobsOverridesInvariants: false

# Restrict AI review (gate.aiReview.mode above) to confirmed Gittensor contributors only. Default:
# false -- AI review runs for EVERY PR author once aiReview.mode is not off, since security/quality
# review should not be weaker for a new or unconfirmed contributor's PR than for an established one's.
# Set true only if you deliberately want to bound AI spend to registered miners; gate.aiReview.allAuthors
# above can then still widen back out to every author within that narrowed mode. Bool. Default: false.
aiReviewConfirmedContributorsOnly: false

# 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
Expand Down
9 changes: 9 additions & 0 deletions apps/gittensory-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -9639,6 +9639,10 @@
"hardGuardrailGlobsOverridesInvariants": {
"type": "boolean",
"nullable": true
},
"aiReviewConfirmedContributorsOnly": {
"type": "boolean",
"nullable": true
}
},
"required": [
Expand Down Expand Up @@ -10362,6 +10366,10 @@
"defaultAllowed",
"commandOverrides"
]
},
"aiReviewConfirmedContributorsOnly": {
"type": "boolean",
"nullable": true
}
},
"required": [
Expand Down Expand Up @@ -10397,6 +10405,7 @@
"aiReviewProvider",
"aiReviewModel",
"aiReviewAllAuthors",
"aiReviewConfirmedContributorsOnly",
"commandAuthorization"
]
},
Expand Down
14 changes: 11 additions & 3 deletions config/examples/loopover.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -350,15 +350,16 @@ gate:
# 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).
# blocker.
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.
# Review every PR author when AI review is enabled. Only has an effect
# when `settings.aiReviewConfirmedContributorsOnly` (below) is also true --
# otherwise every author is already reviewed by default.
# Bool. Default: false.
allAuthors: false
# BYOK provider. anthropic | openai | null. Default: null (use the stored
Expand Down Expand Up @@ -694,6 +695,13 @@ settings:
# the safe-by-default add-only behavior. Bool. Default: false.
hardGuardrailGlobsOverridesInvariants: false

# Restrict AI review (gate.aiReview.mode above) to confirmed Gittensor contributors only. Default:
# false -- AI review runs for EVERY PR author once aiReview.mode is not off, since security/quality
# review should not be weaker for a new or unconfirmed contributor's PR than for an established one's.
# Set true only if you deliberately want to bound AI spend to registered miners; gate.aiReview.allAuthors
# above can then still widen back out to every author within that narrowed mode. Bool. Default: false.
aiReviewConfirmedContributorsOnly: false

# 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
Expand Down
3 changes: 2 additions & 1 deletion packages/gittensory-engine/src/focus-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ export type FocusManifestSettings = Partial<
| "aiReviewProvider"
| "aiReviewModel"
| "aiReviewAllAuthors"
| "aiReviewConfirmedContributorsOnly"
| "closeOwnerAuthors"
| "autoLabelEnabled"
| "typeLabelsEnabled"
Expand Down Expand Up @@ -1857,7 +1858,7 @@ function parseSettingsOverride(value: JsonValue | undefined, warnings: string[],
}
const publicSurface = normalizeOptionalEnum(r.publicSurface, "settings.publicSurface", ["off", "comment_and_label", "comment_only", "label_only"] as const, warnings);
if (publicSurface !== null) out.publicSurface = publicSurface;
for (const key of ["aiReviewByok", "aiReviewAllAuthors", "closeOwnerAuthors", "autoLabelEnabled", "typeLabelsEnabled", "badgeEnabled", "publicQualityMetrics", "createMissingLabel", "includeMaintainerAuthors", "requireLinkedIssue", "backfillEnabled", "agentPaused", "agentDryRun", "hardGuardrailGlobsOverridesInvariants"] as const) {
for (const key of ["aiReviewByok", "aiReviewAllAuthors", "aiReviewConfirmedContributorsOnly", "closeOwnerAuthors", "autoLabelEnabled", "typeLabelsEnabled", "badgeEnabled", "publicQualityMetrics", "createMissingLabel", "includeMaintainerAuthors", "requireLinkedIssue", "backfillEnabled", "agentPaused", "agentDryRun", "hardGuardrailGlobsOverridesInvariants"] as const) {
const flag = normalizeOptionalBoolean(r[key], `settings.${key}`, warnings);
if (flag !== null) out[key] = flag;
}
Expand Down
21 changes: 16 additions & 5 deletions packages/gittensory-engine/src/types/manifest-deps-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,23 @@ export type RepositorySettings = {
/** Config-as-code model override for the BYOK advisory write-up (e.g. "claude-3-5-sonnet-latest").
* `null` = use the key record's model, else a conservative per-provider default. */
aiReviewModel?: string | null | undefined;
/** Review EVERY PR's author, not only confirmed Gittensor contributors. The AI maintainer review is
* confirmed-contributor-gated by default (an AI-spend guard). When true the review runs for any author —
* intended for a self-host operator who wants real reviews on all PRs (incl. their own) and pays for the
* AI themselves. Default false — opt-in via `.gittensory.yml gate.aiReview.allAuthors`. Independent of
* `aiReviewMode`: `off` still means no AI; this only widens WHO an enabled review covers. */
/** Review EVERY PR's author, not only confirmed Gittensor contributors. Only meaningful when
* {@link aiReviewConfirmedContributorsOnly} is also `true` (that field opts INTO confirmed-only
* scoping in the first place — see its own doc comment for the full invariant: AI review runs for
* every author by default, this pair of fields exists purely for a self-host operator who
* deliberately wants to bound AI spend to registered miners). Default false — opt-in via
* `.gittensory.yml gate.aiReview.allAuthors`. Independent of `aiReviewMode`: `off` still means no AI;
* this only widens WHO an enabled review covers, and only within confirmed-contributors-only mode. */
aiReviewAllAuthors: boolean;
/** Opt-in narrowing (config-as-code, self-host operator's own choice — see resolveAiReviewableAuthor
* in src/queue/ai-review-orchestration.ts for the full invariant and history, #orb-ai-review-always-
* review): by default (false/absent) AI review runs for EVERY author once `aiReviewMode !== "off"` —
* security/quality review is not a privilege reserved for confirmed Gittensor miners. Set this `true`
* only if you deliberately want to bound (paid) AI-review spend to confirmed contributors + whatever
* {@link aiReviewAllAuthors}/the `oss-anti-slop`+`block` pack combo widens back in — the ORIGINAL,
* pre-2026-07-14 default behavior, preserved here as an explicit opt-in rather than silently applied
* to everyone. */
aiReviewConfirmedContributorsOnly?: boolean | null | undefined;
/** Configured AI-reviewer confidence floor (0-1) for close calibration (#7). Under `aiReviewMode: block`, AI
* defect findings remain blockers even when their confidence is below this floor; the floor is retained as
* configurable context, not a manual-review downgrade. Config-as-code only — set via `.gittensory.yml
Expand Down
2 changes: 2 additions & 0 deletions src/openapi/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ export const RepositorySettingsSchema = z
aiReviewProvider: z.enum(["anthropic", "openai"]).nullable().optional(),
aiReviewModel: z.string().nullable().optional(),
aiReviewAllAuthors: z.boolean(),
aiReviewConfirmedContributorsOnly: z.boolean().nullable().optional(),
aiReviewCloseConfidence: z.number().nullable().optional(),
aiReviewLowConfidenceDisposition: z.enum(["one_shot", "hold_for_review", "advisory_only"]).nullable().optional(),
aiReviewCombine: z.enum(["single", "consensus", "synthesis"]).nullable().optional(),
Expand Down Expand Up @@ -917,6 +918,7 @@ export const RepoSettingsPreviewSchema = z
aiReviewProvider: z.string().nullable(),
aiReviewModel: z.string().nullable(),
aiReviewAllAuthors: z.boolean(),
aiReviewConfirmedContributorsOnly: z.boolean().nullable(),
commandAuthorization: z.object({
defaultAllowed: z.array(z.enum(["maintainer", "collaborator", "pr_author", "confirmed_miner"])),
commandOverrides: z.array(
Expand Down
52 changes: 35 additions & 17 deletions src/queue/ai-review-orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,35 @@ export async function shouldStartAiReviewForAdvisory(
return !reputationSkip;
}

/** Whether a PR's author is eligible for AI review, given the repo's config (#orb-ai-review-always-review).
*
* INVARIANT: AI review runs for EVERY author by default once a maintainer has opted the repo into AI
* review at all (`aiReviewMode !== "off"`) -- security/quality review is not a privilege reserved for
* confirmed Gittensor miners. The original `confirmedContributor` gate here (#644) was about not
* applying MINER-SPECIFIC gate rules (e.g. a required linked issue) to a non-miner contributor's PR --
* it was never meant to exempt a whole class of authors from AI-driven defect detection, and doing so
* by default left a real gap: an unconfirmed/new contributor's PR got LESS scrutiny than an established
* one's, backwards from what a security posture should look like. Only when a maintainer EXPLICITLY sets
* `aiReviewConfirmedContributorsOnly: true` (opt-in, never the default) does this narrow back down to
* the original confirmed-contributor-only behavior, for a self-host operator who deliberately wants to
* bound AI spend to registered miners. `aiReviewAllAuthors` keeps its original meaning as the
* widen-back-out lever *within* that narrowed mode, unchanged.
*
* Shared by both the "should we even start a review" check (shouldRequirePublicAiReviewForAdvisory) and
* runAiReviewForAdvisory's own execution-time guard, so the two can never drift out of sync the way two
* independently hand-duplicated copies of this same condition previously could.
*/
export function resolveAiReviewableAuthor(
settings: Pick<RepositorySettings, "gatePack" | "aiReviewMode" | "aiReviewConfirmedContributorsOnly"> & {
aiReviewAllAuthors?: boolean | null | undefined;
},
confirmedContributor: boolean,
): boolean {
if (!settings.aiReviewConfirmedContributorsOnly) return true;
const packAllowsAnyAuthorBlockingReview = settings.gatePack === "oss-anti-slop" && settings.aiReviewMode === "block";
return confirmedContributor || packAllowsAnyAuthorBlockingReview || Boolean(settings.aiReviewAllAuthors);
}

export function shouldRequirePublicAiReviewForAdvisory(
env: Env,
args: {
Expand All @@ -205,13 +234,7 @@ export function shouldRequirePublicAiReviewForAdvisory(
skipAiReview?: boolean | undefined;
},
): boolean {
const packAllowsAnyAuthorBlockingReview =
args.settings.gatePack === "oss-anti-slop" &&
args.settings.aiReviewMode === "block";
const reviewableAuthor =
args.confirmedContributor ||
packAllowsAnyAuthorBlockingReview ||
args.settings.aiReviewAllAuthors;
const reviewableAuthor = resolveAiReviewableAuthor(args.settings, args.confirmedContributor);
if (
args.skipAiReview ||
args.settings.aiReviewMode === "off" ||
Expand Down Expand Up @@ -392,16 +415,11 @@ export async function runAiReviewForAdvisory(
}
| undefined
> {
const packAllowsAnyAuthorBlockingReview =
args.settings.gatePack === "oss-anti-slop" &&
args.settings.aiReviewMode === "block";
// `aiReviewAllAuthors` (per-repo opt-in, default false) widens the AI-spend gate to EVERY author — a self-host
// operator who wants real reviews on all PRs (incl. their own / unconfirmed contributors) and pays for the AI
// themselves. Default false ⇒ the confirmed-contributor gate is byte-identical to today.
const reviewableAuthor =
args.confirmedContributor ||
packAllowsAnyAuthorBlockingReview ||
args.settings.aiReviewAllAuthors;
// See resolveAiReviewableAuthor's own doc comment for the full invariant: AI review runs for every
// author by default once aiReviewMode !== "off"; only an explicit aiReviewConfirmedContributorsOnly
// opt-in narrows this back down to confirmed-contributor-or-widened, for a self-host operator who
// deliberately wants to bound AI spend to registered miners.
const reviewableAuthor = resolveAiReviewableAuthor(args.settings, args.confirmedContributor);
if (
args.mode === "paused" ||
args.settings.aiReviewMode === "off" ||
Expand Down
2 changes: 2 additions & 0 deletions src/signals/settings-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export type RepoSettingsPreview = {
aiReviewProvider: string | null;
aiReviewModel: string | null;
aiReviewAllAuthors: boolean;
aiReviewConfirmedContributorsOnly: boolean;
commandAuthorization: {
defaultAllowed: CommandAuthorizationRole[];
commandOverrides: Array<{ command: string; allowedRoles: CommandAuthorizationRole[] }>;
Expand Down Expand Up @@ -353,6 +354,7 @@ export function buildRepoSettingsPreview(args: {
aiReviewProvider: settings.aiReviewProvider ?? null,
aiReviewModel: settings.aiReviewModel ?? null,
aiReviewAllAuthors: settings.aiReviewAllAuthors,
aiReviewConfirmedContributorsOnly: settings.aiReviewConfirmedContributorsOnly ?? false,
commandAuthorization: summarizeCommandAuthorizationPolicy(settings.commandAuthorization),
},
commandAuthorizationPreview,
Expand Down
21 changes: 16 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -859,12 +859,23 @@ export type RepositorySettings = {
/** Config-as-code model override for the BYOK advisory write-up (e.g. "claude-3-5-sonnet-latest").
* `null` = use the key record's model, else a conservative per-provider default. */
aiReviewModel?: string | null | undefined;
/** Review EVERY PR's author, not only confirmed Gittensor contributors. The AI maintainer review is
* confirmed-contributor-gated by default (an AI-spend guard). When true the review runs for any author —
* intended for a self-host operator who wants real reviews on all PRs (incl. their own) and pays for the
* AI themselves. Default false — opt-in via `.gittensory.yml gate.aiReview.allAuthors`. Independent of
* `aiReviewMode`: `off` still means no AI; this only widens WHO an enabled review covers. */
/** Review EVERY PR's author, not only confirmed Gittensor contributors. Only meaningful when
* {@link aiReviewConfirmedContributorsOnly} is also `true` (that field opts INTO confirmed-only
* scoping in the first place — see its own doc comment for the full invariant: AI review runs for
* every author by default, this pair of fields exists purely for a self-host operator who
* deliberately wants to bound AI spend to registered miners). Default false — opt-in via
* `.gittensory.yml gate.aiReview.allAuthors`. Independent of `aiReviewMode`: `off` still means no AI;
* this only widens WHO an enabled review covers, and only within confirmed-contributors-only mode. */
aiReviewAllAuthors: boolean;
/** Opt-in narrowing (config-as-code, self-host operator's own choice — see resolveAiReviewableAuthor
* in src/queue/ai-review-orchestration.ts for the full invariant and history, #orb-ai-review-always-
* review): by default (false/absent) AI review runs for EVERY author once `aiReviewMode !== "off"` —
* security/quality review is not a privilege reserved for confirmed Gittensor miners. Set this `true`
* only if you deliberately want to bound (paid) AI-review spend to confirmed contributors + whatever
* {@link aiReviewAllAuthors}/the `oss-anti-slop`+`block` pack combo widens back in — the ORIGINAL,
* pre-2026-07-14 default behavior, preserved here as an explicit opt-in rather than silently applied
* to everyone. */
aiReviewConfirmedContributorsOnly?: boolean | null | undefined;
/** Configured AI-reviewer confidence floor (0-1) for close calibration (#7). Under `aiReviewMode: block`, AI
* defect findings remain BLOCKERS even when their confidence is below this floor — the floor never turns a
* real defect into a non-blocker on its own. What DOES vary below the floor is governed by the separate
Expand Down
Loading
Loading