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
4 changes: 4 additions & 0 deletions migrations/0076_close_owner_authors.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Per-repo toggle: allow auto-closing the repo OWNER's/maintainer's own PRs (default 0 = exempt, the prior
-- hardwired behavior — owner PRs merge or hold for manual review, never auto-close). Configurable so maintainers
-- aren't locked into one opinion.
ALTER TABLE repository_settings ADD COLUMN close_owner_authors INTEGER NOT NULL DEFAULT 0;
1 change: 1 addition & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,7 @@ export function createApp() {
aiReviewProvider: updated.aiReviewProvider ?? null,
aiReviewModel: updated.aiReviewModel ?? null,
aiReviewAllAuthors: updated.aiReviewAllAuthors,
closeOwnerAuthors: updated.closeOwnerAuthors,
});
});

Expand Down
5 changes: 5 additions & 0 deletions src/db/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ export async function getRepositorySettings(env: Env, fullName: string): Promise
aiReviewProvider: null,
aiReviewModel: null,
aiReviewAllAuthors: false,
closeOwnerAuthors: false,
autoLabelEnabled: true,
gittensorLabel: "gittensor",
blacklistLabel: "slop",
Expand Down Expand Up @@ -475,6 +476,7 @@ export async function getRepositorySettings(env: Env, fullName: string): Promise
aiReviewProvider: normalizeAiReviewProvider(row.aiReviewProvider),
aiReviewModel: row.aiReviewModel ?? null,
aiReviewAllAuthors: row.aiReviewAllAuthors,
closeOwnerAuthors: row.closeOwnerAuthors,
autoLabelEnabled: row.autoLabelEnabled,
gittensorLabel: row.gittensorLabel,
blacklistLabel: row.blacklistLabel,
Expand Down Expand Up @@ -522,6 +524,7 @@ export async function upsertRepositorySettings(env: Env, settings: Partial<Repos
aiReviewProvider: normalizeAiReviewProvider(settings.aiReviewProvider),
aiReviewModel: typeof settings.aiReviewModel === "string" && settings.aiReviewModel.trim() ? settings.aiReviewModel.trim() : null,
aiReviewAllAuthors: settings.aiReviewAllAuthors ?? false,
closeOwnerAuthors: settings.closeOwnerAuthors ?? false,
autoLabelEnabled: settings.autoLabelEnabled ?? true,
gittensorLabel: settings.gittensorLabel ?? "gittensor",
blacklistLabel: settings.blacklistLabel ?? "slop",
Expand Down Expand Up @@ -567,6 +570,7 @@ export async function upsertRepositorySettings(env: Env, settings: Partial<Repos
aiReviewProvider: resolved.aiReviewProvider,
aiReviewModel: resolved.aiReviewModel,
aiReviewAllAuthors: resolved.aiReviewAllAuthors,
closeOwnerAuthors: resolved.closeOwnerAuthors,
autoLabelEnabled: resolved.autoLabelEnabled,
gittensorLabel: resolved.gittensorLabel,
blacklistLabel: resolved.blacklistLabel,
Expand Down Expand Up @@ -613,6 +617,7 @@ export async function upsertRepositorySettings(env: Env, settings: Partial<Repos
aiReviewProvider: resolved.aiReviewProvider,
aiReviewModel: resolved.aiReviewModel,
aiReviewAllAuthors: resolved.aiReviewAllAuthors,
closeOwnerAuthors: resolved.closeOwnerAuthors,
autoLabelEnabled: resolved.autoLabelEnabled,
gittensorLabel: resolved.gittensorLabel,
blacklistLabel: resolved.blacklistLabel,
Expand Down
1 change: 1 addition & 0 deletions src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const repositorySettings = sqliteTable("repository_settings", {
aiReviewProvider: text("ai_review_provider"),
aiReviewModel: text("ai_review_model"),
aiReviewAllAuthors: integer("ai_review_all_authors", { mode: "boolean" }).notNull().default(false),
closeOwnerAuthors: integer("close_owner_authors", { mode: "boolean" }).notNull().default(false),
autoLabelEnabled: integer("auto_label_enabled", { mode: "boolean" }).notNull().default(true),
gittensorLabel: text("gittensor_label").notNull().default("gittensor"),
// Label applied to a blacklisted contributor's PR/issue (#1425); configurable so the disposition works
Expand Down
1 change: 1 addition & 0 deletions src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,7 @@ async function maybeRunAgentMaintenance(
hardGuardrailGlobs,
authorIsOwner,
authorIsAutomationBot,
closeOwnerAuthors: settings.closeOwnerAuthors,
ciState: ciAggregate.ciState,
failingCheckNames: ciAggregate.failingDetails.map((detail) => detail.name),
ciRequiredContextsVerified: hasVerifiedRequiredContexts(requiredContexts),
Expand Down
13 changes: 11 additions & 2 deletions src/settings/agent-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export type AgentActionPlanInput = {
// accumulator like automation/readme-refresh, or dependabot/renovate). These are NEVER auto-closed — a noise
// heuristic (duplicate/slop) must not kill a recurring maintainer-managed PR. They may still auto-merge.
authorIsAutomationBot: boolean;
// Per-repo toggle (#configurable-owner-close): when TRUE, the repo OWNER's own PRs are eligible for auto-close
// like a contributor's (still gated by the `close` autonomy class + adverse-signal conditions). Default/undefined
// ⇒ owner PRs are exempt (merge or manual-hold only). Automation-bot PRs stay exempt regardless.
closeOwnerAuthors?: boolean | undefined;
// Live CI aggregate over ALL of the PR's checks — required OR not, including non-required ones like
// codecov/patch and every commit-status (reviewbot parity). "passed" = every check completed and none
// failed; "failed" = at least one check failed; "pending" = at least one check still running; "unverified"
Expand Down Expand Up @@ -326,6 +330,11 @@ export function planAgentMaintenanceActions(input: AgentActionPlanInput): Planne
const ciUnverified = input.ciState === "unverified";
const reviewGood = gatePassing && ciPassed;
const isContributor = !input.authorIsOwner && !input.authorIsAutomationBot;
// The owner-close exemption is PER-REPO CONFIGURABLE (#configurable-owner-close): by default the repo owner's
// own PRs are exempt from auto-close (closeOwnerAuthors !== true ⇒ merge or manual-hold only), but a maintainer
// can opt in to closing them like a contributor's. Automation bots stay exempt regardless (a noise heuristic
// must not kill a recurring maintainer-managed accumulator).
const closeEligible = isContributor || (input.authorIsOwner && input.closeOwnerAuthors === true);
const mergeableClean = input.pr.mergeableState === "clean";
const isConflict = input.pr.mergeableState === "dirty"; // conflicts with base — can't merge as-is
// RC3: a prior merge attempt failed terminally for THIS exact head SHA (403/405/409/conflict) → never re-plan
Expand All @@ -351,7 +360,7 @@ export function planAgentMaintenanceActions(input: AgentActionPlanInput): Planne
// have folded in optional / third-party checks and must keep the hard-guardrail manual hold.
// (Rebase-if-behind already ran above, so a red CI here is on the latest base — not a stale-base artifact.) (#ci-fail-closes-guarded)
const redVerifiedRequiredCi = ciFailed && input.ciRequiredContextsVerified === true;
const willClose = isContributor && acting("close") && (redVerifiedRequiredCi || (!guardrailHit && (ciFailed || conclusion === "failure" || isConflict)));
const willClose = closeEligible && acting("close") && (redVerifiedRequiredCi || (!guardrailHit && (ciFailed || conclusion === "failure" || isConflict)));
// Linked-issue HARD-RULE close (#linked-issue-hard-rules). A DETERMINISTIC verdict about the LINKED ISSUE
// (owner-assigned / missing point-label / maintainer-only) — NOT an AI verdict, so there is no hallucination
// to guard against: this close fires REGARDLESS of `guardrailHit`. It still only ever closes a CONTRIBUTOR
Expand All @@ -360,7 +369,7 @@ export function planAgentMaintenanceActions(input: AgentActionPlanInput): Planne
const linkedIssueHardRule = input.linkedIssueHardRule;
// Base condition: a CONTRIBUTOR PR links an issue tripping a deterministic hard rule AND the `close` autonomy
// class is acting. (The owner/automation exemption lives in `isContributor`.)
const linkedIssueViolated = linkedIssueHardRule?.violated === true && isContributor && acting("close");
const linkedIssueViolated = linkedIssueHardRule?.violated === true && closeEligible && acting("close");
// Flag-then-close double-check (#linked-issue-verify-before-close). Default behavior when the caller doesn't
// pass the config is IMMEDIATE close (back-compat). When verifyBeforeClose is on, the close is a TWO-PASS
// label-state machine: Pass 1 flags (adds the pending-closure label + a warning comment) and Pass 2 — the next
Expand Down
3 changes: 2 additions & 1 deletion src/signals/focus-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export type FocusManifestSettings = Partial<
| "aiReviewProvider"
| "aiReviewModel"
| "aiReviewAllAuthors"
| "closeOwnerAuthors"
| "autoLabelEnabled"
| "gittensorLabel"
| "createMissingLabel"
Expand Down Expand Up @@ -551,7 +552,7 @@ function parseSettingsOverride(value: JsonValue | undefined, warnings: string[])
if (blacklistLabel !== null) out.blacklistLabel = blacklistLabel;
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", "autoLabelEnabled", "createMissingLabel", "includeMaintainerAuthors", "requireLinkedIssue", "backfillEnabled", "privateTrustEnabled", "agentPaused", "agentDryRun"] as const) {
for (const key of ["aiReviewByok", "aiReviewAllAuthors", "closeOwnerAuthors", "autoLabelEnabled", "createMissingLabel", "includeMaintainerAuthors", "requireLinkedIssue", "backfillEnabled", "privateTrustEnabled", "agentPaused", "agentDryRun"] as const) {
const flag = normalizeOptionalBoolean(r[key], `settings.${key}`, warnings);
if (flag !== null) out[key] = flag;
}
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,11 @@ export type RepositorySettings = {
* 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. */
aiReviewAllAuthors: boolean;
/** When TRUE, the repo OWNER's (and maintainer's) own PRs are eligible for auto-CLOSE like a contributor's
* (still subject to the `close` autonomy class + the same adverse-signal conditions). Default FALSE — owner
* PRs are exempt from auto-close (merge or manual-hold only). Per-repo configurable so maintainers choose
* rather than inheriting a hardwired opinion. */
closeOwnerAuthors: boolean;
autoLabelEnabled: boolean;
gittensorLabel: string;
createMissingLabel: boolean;
Expand Down
10 changes: 10 additions & 0 deletions test/unit/agent-actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,16 @@ describe("planAgentMaintenanceActions (#778)", () => {
const plan = classes(planAgentMaintenanceActions(input({ conclusion: "success", autonomy: { merge: "auto" }, authorIsOwner: true, pr: { labels: [], mergeableState: "clean", reviewDecision: "APPROVED" } })));
expect(plan).toContain("merge");
});

it("DOES auto-close a failing owner PR when closeOwnerAuthors is enabled (per-repo opt-in)", () => {
const plan = classes(planAgentMaintenanceActions(input({ conclusion: "failure", autonomy: { close: "auto" }, blockerTitles: ["x"], authorIsOwner: true, closeOwnerAuthors: true, ciState: "passed", pr: { labels: [], slopRisk: 95 } })));
expect(plan).toContain("close");
});

it("still does NOT close an AUTOMATION-bot PR even when closeOwnerAuthors is enabled (bots stay exempt)", () => {
const plan = classes(planAgentMaintenanceActions(input({ conclusion: "failure", autonomy: { close: "auto" }, blockerTitles: ["x"], authorIsOwner: false, authorIsAutomationBot: true, closeOwnerAuthors: true, ciState: "passed", pr: { labels: [], slopRisk: 95 } })));
expect(plan).not.toContain("close");
});
});

describe("automation-bot guard: never auto-close maintainer-managed accumulator/dependency PRs", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/unit/ai-review-advisory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe("runAiReviewForAdvisory", () => {
// is what lets it through — only the new flag.
const adv = advisory();
const result = await runAiReviewForAdvisory(aiEnv(async () => ({ response: notesOnlyJson() })), {
settings: { aiReviewMode: "advisory", gatePack: "gittensor", aiReviewAllAuthors: true } as RepositorySettings,
settings: { aiReviewMode: "advisory", gatePack: "gittensor", aiReviewAllAuthors: true , closeOwnerAuthors: false} as RepositorySettings,
advisory: adv,
repoFullName: "acme/widgets",
pr,
Expand Down
8 changes: 4 additions & 4 deletions test/unit/focus-manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -924,12 +924,12 @@ describe("parseFocusManifest gate config", () => {
expect((gateConfigToJson(m.gate) as { aiReview: { allAuthors: boolean } }).aiReview.allAuthors).toBe(true);
expect(parseFocusManifest({ gate: gateConfigToJson(m.gate) }).gate).toEqual(m.gate); // round-trips
expect(parseFocusManifest({ gate: { aiReview: { allAuthors: "yes" } } }).warnings.some((w) => /gate\.aiReview\.allAuthors/.test(w))).toBe(true);
const eff = resolveEffectiveSettings({ aiReviewAllAuthors: false } as unknown as RepositorySettings, m);
const eff = resolveEffectiveSettings({ aiReviewAllAuthors: false , closeOwnerAuthors: false} as unknown as RepositorySettings, m);
expect(eff.aiReviewAllAuthors).toBe(true);
// Absent ⇒ null ⇒ the gate alias leaves the DB value untouched.
const noFlag = parseFocusManifest({ gate: { aiReview: { mode: "advisory" } } });
expect(noFlag.gate.aiReviewAllAuthors).toBeNull();
expect(resolveEffectiveSettings({ aiReviewAllAuthors: true } as unknown as RepositorySettings, noFlag).aiReviewAllAuthors).toBe(true);
expect(resolveEffectiveSettings({ aiReviewAllAuthors: true , closeOwnerAuthors: false} as unknown as RepositorySettings, noFlag).aiReviewAllAuthors).toBe(true);
});

it("parses the features: block (per-repo converged-feature toggles), round-trips it, and makes the manifest present", () => {
Expand All @@ -951,9 +951,9 @@ describe("parseFocusManifest gate config", () => {
});

it("parses aiReviewAllAuthors from the settings: block (generic override)", () => {
const parsed = parseFocusManifest({ settings: { aiReviewAllAuthors: true } });
const parsed = parseFocusManifest({ settings: { aiReviewAllAuthors: true , closeOwnerAuthors: false} });
expect(parsed.settings.aiReviewAllAuthors).toBe(true);
expect(resolveEffectiveSettings({ aiReviewAllAuthors: false } as unknown as RepositorySettings, parsed).aiReviewAllAuthors).toBe(true);
expect(resolveEffectiveSettings({ aiReviewAllAuthors: false , closeOwnerAuthors: false} as unknown as RepositorySettings, parsed).aiReviewAllAuthors).toBe(true);
});

it("parses gate.aiReview provider + model (config-as-code) and rejects an unknown provider", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/unit/maintainer-activation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function settings(overrides: Partial<RepositorySettings> = {}): RepositorySettin
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
...overrides,
};
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/policy-sanitizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function settingsFor(repoFullName: string, overrides: Partial<RepositorySettings
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
...overrides,
};
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/registration-readiness.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function settingsFor(repoFullName: string, overrides: Partial<RepositorySettings
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
...overrides,
};
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/repo-policy-readiness.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function settings(overrides: Partial<RepositorySettings> = {}): RepositorySettin
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
...overrides,
};
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/repository-settings-enforcement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function settings(over: Partial<RepositorySettings> = {}): RepositorySettings {
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
aiReviewProvider: null,
aiReviewModel: null,
...over,
Expand Down
2 changes: 1 addition & 1 deletion test/unit/routes-ai-byok.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("maintainer AI-review config route", () => {
env,
);
expect(res.status).toBe(200);
expect(await res.json()).toMatchObject({ aiReviewMode: "block", aiReviewByok: true, aiReviewProvider: "anthropic", aiReviewModel: "claude-3-5-sonnet-latest", aiReviewAllAuthors: true });
expect(await res.json()).toMatchObject({ aiReviewMode: "block", aiReviewByok: true, aiReviewProvider: "anthropic", aiReviewModel: "claude-3-5-sonnet-latest", aiReviewAllAuthors: true , closeOwnerAuthors: false});
const settings = await getRepositorySettings(env, REPO);
expect(settings.aiReviewMode).toBe("block");
expect(settings.aiReviewAllAuthors).toBe(true); // persisted + read back (DB column round-trip)
Expand Down
2 changes: 1 addition & 1 deletion test/unit/self-dogfood-registration-pack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function settingsFor(repoFullName: string, overrides: Partial<RepositorySettings
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
...overrides,
};
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/settings-preview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function settings(overrides: Partial<RepositorySettings> = {}): RepositorySettin
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
...overrides,
};
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/signals-coverage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1851,7 +1851,7 @@ function repoSettings(repoFullName: string): RepositorySettings {
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
};
}

Expand Down
2 changes: 1 addition & 1 deletion test/unit/signals-v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1680,7 +1680,7 @@ describe("v2 signal builders", () => {
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
},
});
expect(comment).toContain("Author: `unknown`");
Expand Down
12 changes: 6 additions & 6 deletions test/unit/signals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ describe("world-class backend signals", () => {
privateTrustEnabled: true,
aiReviewMode: "off" as const,
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
};
const collisions = buildCollisionReport(repo.fullName, issues, pullRequests);
const queueHealth = buildQueueHealth(repo, issues, pullRequests, collisions);
Expand Down Expand Up @@ -461,7 +461,7 @@ describe("world-class backend signals", () => {
privateTrustEnabled: true,
aiReviewMode: "off" as const,
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
};
const collisions = buildCollisionReport(repo.fullName, issues, pullRequests);
const queueHealth = buildQueueHealth(repo, issues, pullRequests, collisions);
Expand Down Expand Up @@ -534,7 +534,7 @@ describe("world-class backend signals", () => {
privateTrustEnabled: true,
aiReviewMode: "off" as const,
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
};
const collisions = buildCollisionReport(repo.fullName, issues, pullRequests);
const queueHealth = buildQueueHealth(repo, issues, pullRequests, collisions);
Expand Down Expand Up @@ -628,7 +628,7 @@ describe("world-class backend signals", () => {
privateTrustEnabled: true,
aiReviewMode: "off" as const,
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
};
const undetected = detectGittensorContributor("newbie", currentPr, [currentPr], []);
const cachedDetected = detectGittensorContributor("oktofeesh1", currentPr, [currentPr, { ...currentPr, number: 10, mergedAt: "2026-05-01T00:00:00.000Z" }], []);
Expand Down Expand Up @@ -697,7 +697,7 @@ describe("world-class backend signals", () => {
privateTrustEnabled: true,
aiReviewMode: "off" as const,
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
};

const comment = buildPublicPrIntelligenceComment({ repo, pr: currentPr, profile, detection, queueHealth, collisions, preflight, settings });
Expand Down Expand Up @@ -810,7 +810,7 @@ describe("world-class backend signals", () => {
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewAllAuthors: false,
aiReviewAllAuthors: false, closeOwnerAuthors: false,
},
});
expect(publicPreflight.findings.map((finding) => finding.code)).toContain("linked_issue_bounty_historical");
Expand Down
Loading
Loading