diff --git a/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.test.tsx b/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.test.tsx
index 9272ee5908..34e87a6ef4 100644
--- a/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.test.tsx
+++ b/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.test.tsx
@@ -37,7 +37,6 @@ const ADVISORY_SETTINGS = {
requireLinkedIssue: false,
commandAuthorization: {},
autonomy: {},
- autoMaintain: { requireApprovals: 1, mergeMethod: "squash" as const },
agentPaused: false,
agentDryRun: false,
};
diff --git a/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.tsx b/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.tsx
index f3769a2bd5..cc2d48e546 100644
--- a/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.tsx
+++ b/apps/loopover-ui/src/components/site/app-panels/gate-ramp-control.tsx
@@ -36,7 +36,6 @@ function normalizeLoadedSettings(data: MaintainerSettingsEditable): MaintainerSe
autonomy: data.autonomy ?? {},
agentPaused: data.agentPaused ?? false,
agentDryRun: data.agentDryRun ?? false,
- autoMaintain: data.autoMaintain ?? { requireApprovals: 1, mergeMethod: "squash" },
};
}
diff --git a/apps/loopover-ui/src/components/site/app-panels/maintainer-settings.tsx b/apps/loopover-ui/src/components/site/app-panels/maintainer-settings.tsx
index 0c7ae19c0a..6433bd1c54 100644
--- a/apps/loopover-ui/src/components/site/app-panels/maintainer-settings.tsx
+++ b/apps/loopover-ui/src/components/site/app-panels/maintainer-settings.tsx
@@ -8,7 +8,6 @@ import { extractPreviewRepoOptions, splitRepoFullName } from "@/lib/maintainer-s
import {
buildMaintainerSettingsSavePayload,
type AgentActionClass,
- type AutoMergeMethod,
type AutonomyLevel,
type CommandRole,
type GateMode,
@@ -195,10 +194,6 @@ export function MaintainerSettings({ reviewability }: { reviewability: Array<{ p
autonomy: result.data.autonomy ?? {},
agentPaused: result.data.agentPaused ?? false,
agentDryRun: result.data.agentDryRun ?? false,
- autoMaintain: result.data.autoMaintain ?? {
- requireApprovals: 1,
- mergeMethod: "squash",
- },
}
: null,
);
@@ -388,41 +383,8 @@ export function MaintainerSettings({ reviewability }: { reviewability: Array<{ p
))}
-
-
- Approvals before auto-merge
-
- setField("autoMaintain", {
- ...settings.autoMaintain,
- requireApprovals: Math.max(0, Math.min(10, Number(event.target.value) || 0)),
- })
- }
- className={FIELD_CLASS}
- />
-
-
- Merge method
-
- setField("autoMaintain", {
- ...settings.autoMaintain,
- mergeMethod: event.target.value as AutoMergeMethod,
- })
- }
- className={FIELD_CLASS}
- >
- merge
- squash
- rebase
-
-
-
+ {/* #6445: "Approvals before auto-merge" / "Merge method" removed -- autoMaintain is no longer
+ DB-backed, config-as-code only via .loopover.yml's settings: block now. */}
>;
- autoMaintain: { requireApprovals: number; mergeMethod: AutoMergeMethod };
+ // #6445: autoMaintain removed -- no longer DB-backed, config-as-code only via .loopover.yml's
+ // settings: block now (the dashboard can no longer write it).
agentPaused: boolean;
agentDryRun: boolean;
};
@@ -61,7 +61,6 @@ export const MAINTAINER_SETTINGS_EDITABLE_KEYS: Array(value, null)).policy;
}
@@ -7798,14 +7746,6 @@ function parseContributorBlacklist(value: string): RepositorySettings["contribut
return normalizeContributorBlacklist(parseJson(value, null)).entries;
}
-function parseAutoCloseExemptLogins(value: string): string[] {
- return normalizeAutoCloseExemptLogins(parseJson(value, null)).logins;
-}
-
-function normalizeReviewNagPolicy(value: string | null | undefined): "off" | "hold" | "close" {
- return value === "hold" || value === "close" ? value : "off";
-}
-
function normalizeDraftPrClosePolicy(value: string | null | undefined): "off" | "close" {
return value === "close" ? "close" : "off";
}
@@ -7836,35 +7776,21 @@ function parseJsonStringArray(value: string): string[] {
return parsed.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
}
-function normalizeCommandRateLimitPolicy(value: string | null | undefined): "off" | "hold" {
- return value === "hold" ? value : "off";
-}
-
function normalizeSkipAutomationBotAuthors(value: string | null | undefined): "inherit" | "off" | "enabled" {
return value === "off" || value === "enabled" ? value : "inherit";
}
-// A review-nag threshold/window is a discrete positive count, not a score — reuses the same non-clamping,
-// non-rounding shape as contributorOpenPrCap's normalizeOpenItemCap (#2270): an invalid value (fractional,
-// non-positive, non-finite) falls back to the given default rather than being silently coerced.
+// Still used by the global moderation config's banThreshold (#selfhost-mod-engine): an invalid value
+// (fractional, non-positive, non-finite) falls back to the given default rather than being silently coerced.
function normalizePositiveIntWithDefault(value: number | null | undefined, fallback: number): number {
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) return fallback;
return value;
}
-function normalizeReviewNagCooldownDays(value: number | null | undefined, fallback: number): number {
- const normalized = normalizePositiveIntWithDefault(value, fallback);
- return Math.min(normalized, MAX_REVIEW_NAG_COOLDOWN_DAYS);
-}
-
function parseAutonomyPolicy(value: string): AutonomyPolicy {
return normalizeAutonomyPolicy(parseJson(value, null));
}
-function parseAutoMaintainPolicy(value: string): AutoMaintainPolicy {
- return normalizeAutoMaintainPolicy(parseJson(value, null));
-}
-
function parseSyncStatus(value: string): RepoSyncStateRecord["status"] {
if (
value === "running" ||
diff --git a/src/db/schema.ts b/src/db/schema.ts
index 5931c364cd..f4681d525b 100644
--- a/src/db/schema.ts
+++ b/src/db/schema.ts
@@ -46,8 +46,6 @@ export const repositories = sqliteTable("repositories", {
export const repositorySettings = sqliteTable("repository_settings", {
repoFullName: text("repo_full_name").primaryKey(),
reviewCheckMode: text("review_check_mode").notNull().default("disabled"),
- projectMilestoneMatchMode: text("project_milestone_match_mode").notNull().default("off"),
- autoProjectMilestoneMatchBackend: text("auto_project_milestone_match_backend").notNull().default("github"),
gatePack: text("gate_pack").notNull().default("gittensor"),
// Missing a linked issue is advisory-only by default -- issues aren't always available, so it only
// blocks when a repo explicitly sets linkedIssueGateMode: "block". The sibling requireLinkedIssue
@@ -92,41 +90,11 @@ export const repositorySettings = sqliteTable("repository_settings", {
requireLinkedIssue: integer("require_linked_issue", { mode: "boolean" }).notNull().default(false),
commandAuthorizationJson: text("command_authorization_json").notNull().default("{}"),
autonomyJson: text("autonomy_json").notNull().default("{}"),
- autoMaintainJson: text("auto_maintain_json").notNull().default("{}"),
agentPaused: integer("agent_paused", { mode: "boolean" }).notNull().default(false),
agentDryRun: integer("agent_dry_run", { mode: "boolean" }).notNull().default(false),
- // Per-contributor open PR/issue caps (#2270, anti-abuse): null = no cap (default). Enforcement lands separately.
- contributorOpenPrCap: integer("contributor_open_pr_cap"),
- contributorOpenIssueCap: integer("contributor_open_issue_cap"),
- contributorCapLabel: text("contributor_cap_label").notNull().default("over-contributor-limit"),
- // Cancel in-flight CI runs on a contributor_cap close (#2462): null = unset, falls back to the
- // CONTRIBUTOR_CAP_CANCEL_CI_DEFAULT env var (nullable, unlike a plain boolean toggle, so an explicit `false`
- // is distinguishable from "not configured" for that fallback). Only meaningful when the App installation has
- // granted actions:write -- degrades gracefully (logs, never blocks the close) otherwise.
- contributorCapCancelCi: integer("contributor_cap_cancel_ci", { mode: "boolean" }),
- // Review-request nagging cooldown (#2463, anti-abuse): default 'off' (disabled).
- reviewNagPolicy: text("review_nag_policy").notNull().default("off"),
- reviewNagMaxPings: integer("review_nag_max_pings").notNull().default(3),
- reviewNagCooldownDays: integer("review_nag_cooldown_days").notNull().default(5),
- reviewNagLabel: text("review_nag_label").notNull().default("review-nag-cooldown"),
- // Maintainer-mention nag moderation (#label-scoping): a JSON array of GitHub logins ALSO throttled under the
- // review-nag cooldown above, on top of the bot's own `@loopover` handle. Default '[]' (no logins watched).
- reviewNagMonitoredMentionsJson: text("review_nag_monitored_mentions_json").notNull().default("[]"),
- // Shared repo-scoped exemption list (#2463): a JSON array of GitHub logins.
- autoCloseExemptLoginsJson: text("auto_close_exempt_logins_json").notNull().default("[]"),
// Force-rebase-before-merge window in minutes (#2552): null = never force (default). Enforcement lands in
// runAgentMaintenancePlanAndExecute, not here.
requireFreshRebaseWindowMinutes: integer("require_fresh_rebase_window_minutes"),
- // Account-age throttle (#2561, anti-abuse): null = off (default). Enforcement lands in
- // runAgentMaintenancePlanAndExecute, not here.
- accountAgeThresholdDays: integer("account_age_threshold_days"),
- newAccountLabel: text("new_account_label").notNull().default("new-account"),
- // Per-command @loopover rate limit (#2560, anti-abuse): generalizes review-nag's cooldown pattern to every
- // command, keyed by (actor, command, targetKey) independent of review-nag's own thread-author-only scope.
- commandRateLimitPolicy: text("command_rate_limit_policy").notNull().default("off"),
- commandRateLimitMaxPerWindow: integer("command_rate_limit_max_per_window").notNull().default(20),
- commandRateLimitAiMaxPerWindow: integer("command_rate_limit_ai_max_per_window").notNull().default(5),
- commandRateLimitWindowHours: integer("command_rate_limit_window_hours").notNull().default(24),
// Draft-PR close policy (#draft-pr-close-policy): off by default -- enforces on ANY draft (including the
// first one, before a review has run), so a maintainer opts in deliberately rather than getting it on by
// default.
diff --git a/test/integration/api.test.ts b/test/integration/api.test.ts
index 26340d97e2..b656733b19 100644
--- a/test/integration/api.test.ts
+++ b/test/integration/api.test.ts
@@ -2635,8 +2635,8 @@ describe("api routes", () => {
// #4618/#5373: gateCheckMode is an unknown key with no effect here (removed from RepositorySettings
// entirely) -- included to confirm it is silently ignored, not to drive reviewCheckMode.
// mergeTrainMode moved off the DB entirely (Batch B, loopover#6443) -- no longer a writable key on this
- // route, config-as-code only via .loopover.yml now.
- body: JSON.stringify({ gateCheckMode: "enabled", reviewCheckMode: "required", slopGateMode: "block", slopGateMinScore: 55, qualityGateMode: "block", autonomy: { merge: "auto_with_approval", deploy: "auto" }, autoMaintain: { requireApprovals: 2, mergeMethod: "rebase" }, agentPaused: true, agentDryRun: true }),
+ // route, config-as-code only via .loopover.yml now. Same for autoMaintain (loopover#6445).
+ body: JSON.stringify({ gateCheckMode: "enabled", reviewCheckMode: "required", slopGateMode: "block", slopGateMinScore: 55, qualityGateMode: "block", autonomy: { merge: "auto_with_approval", deploy: "auto" }, agentPaused: true, agentDryRun: true }),
},
ownerEnv,
);
@@ -2647,7 +2647,6 @@ describe("api routes", () => {
slopGateMinScore: 55,
qualityGateMode: "advisory", // #2267: downgraded, not persisted as "block"
autonomy: { merge: "auto_with_approval" }, // unknown action class dropped by the DB normalizer
- autoMaintain: { requireApprovals: 2, mergeMethod: "rebase" },
agentPaused: true, // #776 kill-switch
agentDryRun: true,
});
@@ -2660,13 +2659,8 @@ describe("api routes", () => {
);
expect(settingsUpdateOff.status).toBe(200);
await expect(settingsUpdateOff.json()).resolves.toMatchObject({ reviewCheckMode: "required" });
- // requireApprovals is bounded at the API boundary — an out-of-range value is rejected, not silently clamped.
- const settingsBadApprovals = await app.request(
- "/v1/repos/repo-owner/owned-repo/settings",
- { method: "PUT", headers: ownerHeaders, body: JSON.stringify({ autoMaintain: { requireApprovals: 99 } }) },
- ownerEnv,
- );
- expect(settingsBadApprovals.status).toBe(400);
+ // autoMaintain moved off the DB entirely (config-as-code, loopover#6445) -- no longer a writable key on
+ // this route, so it's no longer validated at this API boundary either.
const settingsInvalid = await app.request(
"/v1/repos/repo-owner/owned-repo/settings",
{ method: "PUT", headers: ownerHeaders, body: JSON.stringify({ reviewCheckMode: "nonsense" }) },
diff --git a/test/integration/maintainer-activation.test.ts b/test/integration/maintainer-activation.test.ts
index 35c2c19979..1d9adbfa8e 100644
--- a/test/integration/maintainer-activation.test.ts
+++ b/test/integration/maintainer-activation.test.ts
@@ -143,11 +143,13 @@ describe("maintainer activation routes", () => {
const response = await app.request(`${PATH_ACTIVATE.replace("/activation", "/settings")}`, {
method: "PUT",
headers: { cookie: `loopover_session=${token}`, "content-type": "application/json" },
- body: JSON.stringify({ autonomy: { merge: "auto_with_approval" }, autoMaintain: { requireApprovals: 2, mergeMethod: "rebase" } }),
+ // autoMaintain moved off the DB entirely (config-as-code, loopover#6445) -- no longer a writable key on
+ // this route.
+ body: JSON.stringify({ autonomy: { merge: "auto_with_approval" } }),
}, env);
expect(response.status).toBe(200);
- expect(await response.json()).toMatchObject({ autonomy: { merge: "auto_with_approval" }, autoMaintain: { requireApprovals: 2, mergeMethod: "rebase" } });
+ expect(await response.json()).toMatchObject({ autonomy: { merge: "auto_with_approval" } });
});
it("persists selfAuthoredLinkedIssueGateMode from the settings PUT (API/OpenAPI parity)", async () => {
diff --git a/test/unit/agent-approval-queue.test.ts b/test/unit/agent-approval-queue.test.ts
index b37ac63866..8b9d0845ad 100644
--- a/test/unit/agent-approval-queue.test.ts
+++ b/test/unit/agent-approval-queue.test.ts
@@ -640,7 +640,9 @@ describe("agent approval queue (#779)", () => {
it("accept re-syncs the merge method to the CURRENT repo config, not the staging-time snapshot (#2131)", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: "x" });
// Staged while the default was "squash"; the maintainer has since changed the repo's default to "merge".
- await upsertRepositorySettings(env, { repoFullName: "owner/repo", autonomy: { merge: "auto_with_approval" }, autoMaintain: { mergeMethod: "merge", requireApprovals: 0 } });
+ // autoMaintain moved off the DB entirely (config-as-code, loopover#6445) -- set via manifest injection.
+ await upsertRepositorySettings(env, { repoFullName: "owner/repo", autonomy: { merge: "auto_with_approval" } });
+ await upsertRepoFocusManifest(env, "owner/repo", { settings: { autoMaintain: { mergeMethod: "merge", requireApprovals: 0 } } });
await seedInstallation(env);
await upsertPullRequestFromGitHub(env, "owner/repo", { number: 7, title: "PR", state: "open", user: { login: "contributor" }, head: { sha: "h7" }, labels: [], body: "x" });
const { action } = await createPendingAgentActionIfAbsent(env, { repoFullName: "owner/repo", pullNumber: 7, installationId: 5, actionClass: "merge", autonomyLevel: "auto_with_approval", params: { mergeMethod: "squash", expectedHeadSha: "h7" }, reason: "clean" });
diff --git a/test/unit/data-spine.test.ts b/test/unit/data-spine.test.ts
index 7090cdf6e7..7b856b46c1 100644
--- a/test/unit/data-spine.test.ts
+++ b/test/unit/data-spine.test.ts
@@ -45,6 +45,8 @@ import {
upsertRepositoryFromGitHub,
upsertRepositorySettings,
} from "../../src/db/repositories";
+import { resolveRepositorySettings } from "../../src/settings/repository-settings";
+import { upsertRepoFocusManifest } from "../../src/signals/focus-manifest-loader";
import { createTestEnv } from "../helpers/d1";
describe("data spine repositories", () => {
@@ -297,11 +299,13 @@ describe("data spine repositories", () => {
await upsertRepositorySettings(env, { repoFullName: "owner/autonomyrepo", autonomy: { merge: "observe" } });
expect((await getRepositorySettings(env, "owner/autonomyrepo")).autonomy).toEqual({ merge: "observe" }); // update persists
expect((await getRepositorySettings(env, "owner/defaultpack")).autonomy).toEqual({}); // deny-by-default
- // #774 autoMaintain round-trips, clamps requireApprovals, and defaults to squash/1.
+ // #774/loopover#6445: autoMaintain moved off the DB entirely (config-as-code only via .loopover.yml's
+ // settings: block now) -- getRepositorySettings always resolves the built-in squash/1 default,
+ // ignoring any caller-supplied override.
await upsertRepositorySettings(env, { repoFullName: "owner/automaintainrepo", autoMaintain: { requireApprovals: 99, mergeMethod: "rebase" } });
- expect((await getRepositorySettings(env, "owner/automaintainrepo")).autoMaintain).toEqual({ requireApprovals: 10, mergeMethod: "rebase" });
+ expect((await getRepositorySettings(env, "owner/automaintainrepo")).autoMaintain).toEqual({ requireApprovals: 1, mergeMethod: "squash" });
await upsertRepositorySettings(env, { repoFullName: "owner/automaintainrepo", autoMaintain: { requireApprovals: 0, mergeMethod: "merge" } });
- expect((await getRepositorySettings(env, "owner/automaintainrepo")).autoMaintain).toEqual({ requireApprovals: 0, mergeMethod: "merge" }); // update persists
+ expect((await getRepositorySettings(env, "owner/automaintainrepo")).autoMaintain).toEqual({ requireApprovals: 1, mergeMethod: "squash" });
expect((await getRepositorySettings(env, "owner/defaultpack")).autoMaintain).toEqual({ requireApprovals: 1, mergeMethod: "squash" }); // defaults
// #776 kill-switch + dry-run round-trip (insert + update) and default false.
await upsertRepositorySettings(env, { repoFullName: "owner/saferepo", agentPaused: true, agentDryRun: true });
@@ -309,30 +313,31 @@ describe("data spine repositories", () => {
await upsertRepositorySettings(env, { repoFullName: "owner/saferepo", agentPaused: false });
expect((await getRepositorySettings(env, "owner/saferepo")).agentPaused).toBe(false); // update persists
expect(await getRepositorySettings(env, "owner/defaultpack")).toMatchObject({ agentPaused: false, agentDryRun: false }); // defaults
- // #2270 per-contributor open PR/issue caps: no row and no cap set both default to null (disabled).
+ // #2270/loopover#6445: per-contributor open PR/issue caps moved off the DB entirely -- config-as-code
+ // only via .loopover.yml's settings: block now. getRepositorySettings always resolves the conservative
+ // null (disabled) default, ignoring any caller-supplied override; no row and no cap set both default to
+ // null (disabled) too.
expect(await getRepositorySettings(env, "missing/repo")).toMatchObject({ contributorOpenPrCap: null, contributorOpenIssueCap: null });
expect(await getRepositorySettings(env, "owner/defaultpack")).toMatchObject({ contributorOpenPrCap: null, contributorOpenIssueCap: null });
- // Round-trips on insert and persists on update.
await upsertRepositorySettings(env, { repoFullName: "owner/caprepo", contributorOpenPrCap: 2, contributorOpenIssueCap: 5 });
- expect(await getRepositorySettings(env, "owner/caprepo")).toMatchObject({ contributorOpenPrCap: 2, contributorOpenIssueCap: 5 });
- await upsertRepositorySettings(env, { repoFullName: "owner/caprepo", contributorOpenPrCap: 3, contributorOpenIssueCap: null });
- expect(await getRepositorySettings(env, "owner/caprepo")).toMatchObject({ contributorOpenPrCap: 3, contributorOpenIssueCap: null }); // update persists + can clear
- await upsertRepositorySettings(env, { repoFullName: "owner/caprepo", contributorOpenPrCap: 101, contributorOpenIssueCap: 150 });
- expect(await getRepositorySettings(env, "owner/caprepo")).toMatchObject({ contributorOpenPrCap: 100, contributorOpenIssueCap: 100 }); // clamps to the live-check sample budget
- // A cap must be a positive whole number: fractional, non-positive, and non-finite values are all
- // dropped to null rather than silently coerced (there's no such thing as "allow 2.5 open PRs").
- await upsertRepositorySettings(env, { repoFullName: "owner/badcaprepo", contributorOpenPrCap: 2.5 as never });
- expect((await getRepositorySettings(env, "owner/badcaprepo")).contributorOpenPrCap).toBeNull();
- await upsertRepositorySettings(env, { repoFullName: "owner/badcaprepo", contributorOpenPrCap: 0 });
- expect((await getRepositorySettings(env, "owner/badcaprepo")).contributorOpenPrCap).toBeNull();
- await upsertRepositorySettings(env, { repoFullName: "owner/badcaprepo", contributorOpenPrCap: Number.NaN as never });
- expect((await getRepositorySettings(env, "owner/badcaprepo")).contributorOpenPrCap).toBeNull();
- // contributorCapLabel (#2270) round-trips and defaults to "over-contributor-limit".
+ expect(await getRepositorySettings(env, "owner/caprepo")).toMatchObject({ contributorOpenPrCap: null, contributorOpenIssueCap: null });
+ // resolveRepositorySettings honors an explicit manifest override, including the same validation the DB
+ // layer used to enforce: a cap must be a positive whole number (fractional/non-positive/non-finite all
+ // drop to null -- there's no such thing as "allow 2.5 open PRs"), clamped to the live-verification
+ // sample budget of 100.
+ await upsertRepoFocusManifest(env, "owner/caprepo", { settings: { contributorOpenPrCap: 3, contributorOpenIssueCap: 150 } });
+ expect(await resolveRepositorySettings(env, "owner/caprepo")).toMatchObject({ contributorOpenPrCap: 3, contributorOpenIssueCap: 100 });
+ await upsertRepoFocusManifest(env, "owner/badcaprepo", { settings: { contributorOpenPrCap: 2.5 as never } });
+ expect((await resolveRepositorySettings(env, "owner/badcaprepo")).contributorOpenPrCap).toBeNull();
+ await upsertRepoFocusManifest(env, "owner/badcaprepo", { settings: { contributorOpenPrCap: 0 } });
+ expect((await resolveRepositorySettings(env, "owner/badcaprepo")).contributorOpenPrCap).toBeNull();
+ // contributorCapLabel (#2270/loopover#6445): moved off the DB entirely too -- defaults to
+ // "over-contributor-limit", ignores a caller-supplied DB override, honors a manifest override.
expect((await getRepositorySettings(env, "missing/repo")).contributorCapLabel).toBe("over-contributor-limit");
await upsertRepositorySettings(env, { repoFullName: "owner/caprepo", contributorCapLabel: "spam-cap" });
- expect((await getRepositorySettings(env, "owner/caprepo")).contributorCapLabel).toBe("spam-cap");
- await upsertRepositorySettings(env, { repoFullName: "owner/caprepo", contributorCapLabel: "renamed-cap" });
- expect((await getRepositorySettings(env, "owner/caprepo")).contributorCapLabel).toBe("renamed-cap"); // update persists
+ expect((await getRepositorySettings(env, "owner/caprepo")).contributorCapLabel).toBe("over-contributor-limit");
+ await upsertRepoFocusManifest(env, "owner/caprepo", { settings: { contributorCapLabel: "renamed-cap" } });
+ expect((await resolveRepositorySettings(env, "owner/caprepo")).contributorCapLabel).toBe("renamed-cap");
// #2552 force-rebase-before-merge window: no row and no override both default to null (never force).
expect((await getRepositorySettings(env, "missing/repo")).requireFreshRebaseWindowMinutes).toBeNull();
expect((await getRepositorySettings(env, "owner/defaultpack")).requireFreshRebaseWindowMinutes).toBeNull();
@@ -347,13 +352,17 @@ describe("data spine repositories", () => {
// contributorOpenPrCap above -- it must NOT inherit that unrelated cap's 100 ceiling.
await upsertRepositorySettings(env, { repoFullName: "owner/rebasewindowrepo", requireFreshRebaseWindowMinutes: 500 });
expect((await getRepositorySettings(env, "owner/rebasewindowrepo")).requireFreshRebaseWindowMinutes).toBe(500);
- // #1936 minimum account-age gate: no row and no override both default to null (never enforced); round-trips,
- // and (REGRESSION, same reasoning as requireFreshRebaseWindowMinutes above) is not capped at 100.
+ // #1936/loopover#6445: minimum account-age gate moved off the DB entirely -- config-as-code only via
+ // .loopover.yml's settings: block now. No row and no override both default to null (never enforced);
+ // a caller-supplied DB override is ignored; resolveRepositorySettings honors a manifest override
+ // (still not capped at 100, unlike the live-verification-sample-bounded caps above).
expect((await getRepositorySettings(env, "missing/repo")).accountAgeThresholdDays).toBeNull();
await upsertRepositorySettings(env, { repoFullName: "owner/accountagerepo", accountAgeThresholdDays: 365 });
- expect((await getRepositorySettings(env, "owner/accountagerepo")).accountAgeThresholdDays).toBe(365);
- // #2463 review-nag cooldown + shared exemption list: no row and no override both default to off/3/5/the
- // default label/empty exemption list.
+ expect((await getRepositorySettings(env, "owner/accountagerepo")).accountAgeThresholdDays).toBeNull();
+ await upsertRepoFocusManifest(env, "owner/accountagerepo", { settings: { accountAgeThresholdDays: 365 } });
+ expect((await resolveRepositorySettings(env, "owner/accountagerepo")).accountAgeThresholdDays).toBe(365);
+ // #2463/loopover#6445: review-nag cooldown + shared exemption list moved off the DB entirely too. No row
+ // and no override both default to off/3/5/the default label/empty exemption list.
expect(await getRepositorySettings(env, "missing/repo")).toMatchObject({
reviewNagPolicy: "off",
reviewNagMaxPings: 3,
@@ -362,7 +371,7 @@ describe("data spine repositories", () => {
autoCloseExemptLogins: [],
});
expect(await getRepositorySettings(env, "owner/defaultpack")).toMatchObject({ reviewNagPolicy: "off", autoCloseExemptLogins: [] });
- // Round-trips on insert and persists on update.
+ // A caller-supplied DB override is silently ignored -- always the built-in defaults.
await upsertRepositorySettings(env, {
repoFullName: "owner/nagrepo",
reviewNagPolicy: "close",
@@ -372,22 +381,42 @@ describe("data spine repositories", () => {
autoCloseExemptLogins: ["Trusted-Regular"],
});
expect(await getRepositorySettings(env, "owner/nagrepo")).toMatchObject({
+ reviewNagPolicy: "off",
+ reviewNagMaxPings: 3,
+ reviewNagCooldownDays: 5,
+ reviewNagLabel: "review-nag-cooldown",
+ autoCloseExemptLogins: [],
+ });
+ // resolveRepositorySettings honors an explicit manifest override, round-tripping through a re-upsert
+ // that carries it forward explicitly (upsertRepoFocusManifest replaces the manifest wholesale).
+ await upsertRepoFocusManifest(env, "owner/nagrepo", {
+ settings: {
+ reviewNagPolicy: "close",
+ reviewNagMaxPings: 5,
+ reviewNagCooldownDays: 10,
+ reviewNagLabel: "too-many-pings",
+ autoCloseExemptLogins: ["Trusted-Regular"],
+ },
+ });
+ expect(await resolveRepositorySettings(env, "owner/nagrepo")).toMatchObject({
reviewNagPolicy: "close",
reviewNagMaxPings: 5,
reviewNagCooldownDays: 10,
reviewNagLabel: "too-many-pings",
autoCloseExemptLogins: ["Trusted-Regular"],
});
- await upsertRepositorySettings(env, { repoFullName: "owner/nagrepo", reviewNagPolicy: "hold", autoCloseExemptLogins: [] });
- expect(await getRepositorySettings(env, "owner/nagrepo")).toMatchObject({ reviewNagPolicy: "hold", autoCloseExemptLogins: [] }); // update persists + can clear
+ await upsertRepoFocusManifest(env, "owner/nagrepo", { settings: { reviewNagPolicy: "hold", autoCloseExemptLogins: [] } });
+ expect(await resolveRepositorySettings(env, "owner/nagrepo")).toMatchObject({ reviewNagPolicy: "hold", autoCloseExemptLogins: [] }); // update persists + can clear
// An invalid policy string is dropped to "off"; a non-positive/fractional ping count or cooldown falls
// back to its default rather than being silently coerced.
- await upsertRepositorySettings(env, { repoFullName: "owner/badnagrepo", reviewNagPolicy: "delete-everything" as never, reviewNagMaxPings: -1, reviewNagCooldownDays: 2.5 as never });
- expect(await getRepositorySettings(env, "owner/badnagrepo")).toMatchObject({ reviewNagPolicy: "off", reviewNagMaxPings: 3, reviewNagCooldownDays: 5 });
- await upsertRepositorySettings(env, { repoFullName: "owner/bigwindowrepo", reviewNagMaxPings: 1_000, reviewNagCooldownDays: 1_000_000_000 });
- expect(await getRepositorySettings(env, "owner/bigwindowrepo")).toMatchObject({ reviewNagMaxPings: 1_000, reviewNagCooldownDays: 365 });
- await env.DB.prepare("update repository_settings set review_nag_cooldown_days = ? where repo_full_name = ?").bind(1_000_000_000, "owner/bigwindowrepo").run();
- expect(await getRepositorySettings(env, "owner/bigwindowrepo")).toMatchObject({ reviewNagMaxPings: 1_000, reviewNagCooldownDays: 365 });
+ await upsertRepoFocusManifest(env, "owner/badnagrepo", { settings: { reviewNagPolicy: "delete-everything" as never, reviewNagMaxPings: -1, reviewNagCooldownDays: 2.5 as never } });
+ expect(await resolveRepositorySettings(env, "owner/badnagrepo")).toMatchObject({ reviewNagPolicy: "off", reviewNagMaxPings: 3, reviewNagCooldownDays: 5 });
+ // REGRESSION: a cooldown ABOVE the 365-day ceiling is rejected outright (falls back to the built-in
+ // default), unlike the old DB-layer behavior which clamped it down to 365 -- the manifest parser's
+ // normalizeOptionalPositiveInteger + explicit ceiling check drops an out-of-range value rather than
+ // silently coercing it.
+ await upsertRepoFocusManifest(env, "owner/bigwindowrepo", { settings: { reviewNagMaxPings: 1_000, reviewNagCooldownDays: 1_000_000_000 } });
+ expect(await resolveRepositorySettings(env, "owner/bigwindowrepo")).toMatchObject({ reviewNagMaxPings: 1_000, reviewNagCooldownDays: 5 });
expect(updated.slopAiAdvisory).toBe(false);
expect(await getRepoSyncState(env, "missing/repo")).toBeNull();
expect(await getPullRequest(env, "owner/repo", 404)).toBeNull();
diff --git a/test/unit/queue-3.test.ts b/test/unit/queue-3.test.ts
index 22a3fd6a60..4480a58f56 100644
--- a/test/unit/queue-3.test.ts
+++ b/test/unit/queue-3.test.ts
@@ -2550,11 +2550,10 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "owner/repo",
autonomy: opts.autonomy ?? { merge: "auto", update_branch: "auto", label: "auto" },
- autoMaintain: { requireApprovals: 0, mergeMethod: "squash" },
gatePack: "oss-anti-slop",
...(opts.requireFreshRebaseWindowMinutes !== undefined ? { requireFreshRebaseWindowMinutes: opts.requireFreshRebaseWindowMinutes } : {}),
});
- await upsertRepoFocusManifest(env, "owner/repo", { settings: { checkRunMode: "off", commentMode: "off", publicSurface: "off", aiReviewMode: "off", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "owner/repo", { settings: { checkRunMode: "off", commentMode: "off", publicSurface: "off", aiReviewMode: "off", reviewCheckMode: "required", autoMaintain: { requireApprovals: 0, mergeMethod: "squash" } } });
await upsertPullRequestFromGitHub(env, "owner/repo", { number: prNumber, title: "Fresh rebase PR", state: "open", user: { login: "contributor" }, head: { sha: "sha1" }, base: { ref: "main" }, labels: [], body: "" });
}
@@ -2706,10 +2705,9 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
// The label is the configurable `.loopover.yml` value below — nothing is hard-coded.
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", contributorCapLabel: "spam-cap", reviewCheckMode: "required", aiReviewMode: "advisory" } }, "repo_file");
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", contributorCapLabel: "spam-cap", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } }, "repo_file");
const seen = { closed: false, labels: [] as string[], comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -2771,10 +2769,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
- autoCloseExemptLogins: ["sentry[bot]"],
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2, autoCloseExemptLogins: ["sentry[bot]"] } });
const seen = { closed: false, labels: [] as string[], comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -2853,10 +2849,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
- contributorCapCancelCi: true,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2, contributorCapCancelCi: true } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal("fetch", stubContributorCapCiCancelFetch(seen, { in_progress: [101], queued: [102] }));
@@ -2890,10 +2884,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
- contributorCapCancelCi: true,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2, contributorCapCancelCi: true } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal("fetch", stubContributorCapCiCancelFetch(seen, { in_progress: [103] }));
const originalRecordAuditEvent = repositoriesModule.recordAuditEvent;
@@ -2930,10 +2922,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
- contributorCapCancelCi: true,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2, contributorCapCancelCi: true } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal(
"fetch",
@@ -2973,10 +2963,9 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
// contributorCapCancelCi intentionally omitted — off by default, no CONTRIBUTOR_CAP_CANCEL_CI_DEFAULT set.
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal("fetch", stubContributorCapCiCancelFetch(seen, { in_progress: [201] }));
@@ -3008,10 +2997,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
- contributorCapCancelCi: true,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2, contributorCapCancelCi: true } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -3052,10 +3039,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
- contributorCapCancelCi: true,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2, contributorCapCancelCi: true } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal(
"fetch",
@@ -3092,10 +3077,9 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
// contributorCapCancelCi intentionally omitted (null) -- falls back to the env var default above.
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal("fetch", stubContributorCapCiCancelFetch(seen, { in_progress: [301] }));
@@ -3126,10 +3110,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
- contributorCapCancelCi: false,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2, contributorCapCancelCi: false } });
const seen = { closed: false, cancelledIds: [] as number[], listedStatuses: [] as string[] };
vi.stubGlobal("fetch", stubContributorCapCiCancelFetch(seen, { in_progress: [401] }));
@@ -3163,9 +3145,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 1,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 1 } });
const seen = { closed: false, comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -3217,9 +3198,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 100, // above SIBLING_COUNT + 1 — this test only cares about concurrency, not closing.
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "off", publicSurface: "off", checkRunMode: "off", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "off", publicSurface: "off", checkRunMode: "off", reviewCheckMode: "required", contributorOpenPrCap: 100 } });
let inFlight = 0;
let maxInFlight = 0;
const siblingCheckPattern = new RegExp(`/pulls/(?:${Array.from({ length: SIBLING_COUNT }, (_, i) => i + 1).join("|")})$`);
@@ -3327,9 +3307,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -3561,9 +3540,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/repo-a",
autonomy: { close: "auto", label: "auto" },
- autoCloseExemptLogins: ["farmer99"],
});
- await upsertRepoFocusManifest(env, "JSONbored/repo-a", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/repo-a", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", autoCloseExemptLogins: ["farmer99"] } });
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -3730,9 +3708,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -3782,9 +3759,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -3862,10 +3838,8 @@ describe("queue processors", () => {
repoFullName: "JSONbored/gittensory",
// #label-scoping: the cap label/close rides on `close`; the new-account label rides on `review_state_label`.
autonomy: { close: "auto", review_state_label: "auto" },
- contributorOpenPrCap: 4,
- accountAgeThresholdDays: 30,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required", contributorOpenPrCap: 4, accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
// Account created 2 days ago — well under the 30-day threshold.
vi.stubGlobal("fetch", stubAccountAgeFetch(62, new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -3897,10 +3871,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- contributorOpenPrCap: 2,
- accountAgeThresholdDays: 30,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required", contributorOpenPrCap: 2, accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -3950,10 +3922,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 4,
- accountAgeThresholdDays: 30,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required", contributorOpenPrCap: 4, accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
// Account created 2 years ago — well over the 30-day threshold.
vi.stubGlobal("fetch", stubAccountAgeFetch(65, new Date(Date.now() - 730 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -3983,9 +3953,9 @@ describe("queue processors", () => {
});
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
- accountAgeThresholdDays: 30,
+ reviewCheckMode: "required",
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required", accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4083,10 +4053,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- accountAgeThresholdDays: 30,
- newAccountLabel: "custom-new-account-label",
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required", accountAgeThresholdDays: 30, newAccountLabel: "custom-new-account-label" } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", stubAccountAgeFetch(68, new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -4115,9 +4083,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
// autonomy intentionally omitted — deny-by-default ("observe" for every action class, including "review_state_label").
- accountAgeThresholdDays: 30,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", reviewCheckMode: "required", accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", stubAccountAgeFetch(69, new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -4169,9 +4136,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- contributorOpenIssueCap: 4,
- accountAgeThresholdDays: 30,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 4, accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", stubIssueAccountAgeFetch(62, new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -4202,8 +4168,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- contributorOpenIssueCap: 4,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 4 } });
let accountAgeUsersFetched = false;
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4242,9 +4208,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- contributorOpenIssueCap: 4,
- accountAgeThresholdDays: 30,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 4, accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", stubIssueAccountAgeFetch(62, new Date(Date.now() - 730 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -4275,9 +4240,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto" },
- contributorOpenIssueCap: 4,
- accountAgeThresholdDays: 30,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 4, accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", stubIssueAccountAgeFetch(62, new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -4308,9 +4272,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- contributorOpenIssueCap: 4,
- accountAgeThresholdDays: 30,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 4, accountAgeThresholdDays: 30 } });
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4347,9 +4310,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- accountAgeThresholdDays: 30,
- newAccountLabel: "custom-new-account-label",
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { accountAgeThresholdDays: 30, newAccountLabel: "custom-new-account-label" } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", stubIssueAccountAgeFetch(62, new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), seen));
@@ -4378,8 +4340,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- accountAgeThresholdDays: 30,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4421,8 +4383,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- accountAgeThresholdDays: 30,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4461,8 +4423,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", review_state_label: "auto" },
- accountAgeThresholdDays: 30,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { accountAgeThresholdDays: 30 } });
const seen = { labels: [] as string[], closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4508,9 +4470,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const closedNumbers = new Set();
const fanned: import("../../src/types").JobMessage[] = [];
const realSend = env.JOBS.send.bind(env.JOBS);
@@ -4591,9 +4552,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const fanned: import("../../src/types").JobMessage[] = [];
const realSend = env.JOBS.send.bind(env.JOBS);
env.JOBS.send = (async (message: import("../../src/types").JobMessage, options?: QueueSendOptions) => {
@@ -4644,9 +4604,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
env.JOBS.send = (async () => {
throw new Error("queue send boom");
}) as typeof env.JOBS.send;
@@ -4701,9 +4660,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const fanned: import("../../src/types").JobMessage[] = [];
const realSend = env.JOBS.send.bind(env.JOBS);
env.JOBS.send = (async (message: import("../../src/types").JobMessage, options?: QueueSendOptions) => {
@@ -4777,9 +4735,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenPrCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commentMode: "all_prs", publicSurface: "comment_only", checkRunMode: "off", reviewCheckMode: "required", aiReviewMode: "advisory", contributorOpenPrCap: 2 } });
const realGetPullRequest = repositoriesModule.getPullRequest;
vi.spyOn(repositoriesModule, "getPullRequest").mockImplementation(async (spyEnv, fullName, number) => {
if (number === 56) return null; // simulate PR56 not being found locally at wake time
@@ -4838,9 +4795,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenIssueCap: 2,
});
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorCapLabel: "spam-cap" } }, "repo_file");
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorCapLabel: "spam-cap", contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false, labels: [] as string[], comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4891,8 +4847,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenIssueCap: SIBLING_COUNT,
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: SIBLING_COUNT } }, "repo_file");
let inFlight = 0;
let maxInFlight = 0;
let closed = false;
@@ -4941,9 +4897,8 @@ describe("queue processors", () => {
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto", label: "auto" },
- contributorOpenIssueCap: 2,
- autoCloseExemptLogins: ["sentry[bot]"],
});
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2, autoCloseExemptLogins: ["sentry[bot]"] } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -4984,7 +4939,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Farmer issue one (stale-open)", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5027,7 +4983,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Farmer issue one", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5066,8 +5023,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Farmer issue one", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorCapLabel: "spam-cap" } }, "repo_file");
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorCapLabel: "spam-cap", contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5104,7 +5061,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Farmer issue one (stale-open)", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false, sawPublicToken: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5348,7 +5306,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "JSONbored/repo-a", { number: 60, title: "Farmer issue one", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/repo-a", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/repo-a", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/repo-a", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/repo-a", { settings: { contributorOpenIssueCap: 2 } });
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5390,7 +5349,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Farmer issue one", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5472,7 +5432,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Owner issue one", state: "open", user: { login: "JSONbored" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 61, title: "Owner issue two", state: "open", user: { login: "JSONbored" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5505,7 +5466,8 @@ describe("queue processors", () => {
});
// Only ONE pre-existing open issue from this author — the incoming issue is their 2nd, exactly at the cap.
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Farmer issue one", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5541,7 +5503,8 @@ describe("queue processors", () => {
// No acting autonomy for label/close — deny-by-default (autonomy: {}) means planAgentMaintenanceActions
// plans nothing at all, so this exercises the "planned.length === 0" early return distinctly from the
// disabled-cap case above (here the cap DOES match; there is simply nothing to execute).
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: {}, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: {} });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -5583,7 +5546,8 @@ describe("queue processors", () => {
});
await upsertIssueFromGitHub(env, "noslash", { number: 60, title: "Farmer issue one", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "noslash", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "noslash", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "noslash", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "noslash", { settings: { contributorOpenIssueCap: 2 } });
vi.stubGlobal("fetch", async (input: RequestInfo | URL) => {
const url = input.toString();
if (url.includes("/access_tokens")) return Response.json({ token: "installation-token" });
@@ -5620,7 +5584,8 @@ describe("queue processors", () => {
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 59, title: "Ghost issue", state: "open", labels: [], body: "z" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 60, title: "Farmer issue one", state: "open", user: { login: "farmer99" }, labels: [], body: "x" });
await upsertIssueFromGitHub(env, "JSONbored/gittensory", { number: 61, title: "Farmer issue two", state: "open", user: { login: "farmer99" }, labels: [], body: "y" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" }, contributorOpenIssueCap: 2 });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { contributorOpenIssueCap: 2 } }, "repo_file");
const seen = { closed: false };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
diff --git a/test/unit/queue-5.test.ts b/test/unit/queue-5.test.ts
index 3413f4d58d..1fdbe35dc5 100644
--- a/test/unit/queue-5.test.ts
+++ b/test/unit/queue-5.test.ts
@@ -303,7 +303,7 @@ describe("queue processors", () => {
// getRepositorySettings neutralizes it first. Mock resolveRepositorySettings directly so this test proves
// processors.ts's OWN Math.min(reviewNagCooldownDays, MAX_REVIEW_NAG_COOLDOWN_DAYS) guard, not the DB layer.
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "hold", reviewNagMaxPings: 3 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "hold", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 206, title: "Huge cooldown", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
// Three prior pings, all 400 DAYS ago -- outside the 365-day cap, but well within an uncapped
// "1,000,000,000-day" window. If the guard clamps correctly, these fall outside the window and don't
@@ -346,7 +346,7 @@ describe("queue processors", () => {
it("records pings under the configured threshold without acting; the normal @loopover reply still proceeds", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 201, title: "Under threshold", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubReviewNagFetch(201, seen);
@@ -375,7 +375,7 @@ describe("queue processors", () => {
it("hold policy: posts a cooldown reply and short-circuits once the threshold is crossed", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "hold", reviewNagMaxPings: 3, reviewNagCooldownDays: 5 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "hold", reviewNagMaxPings: 3, reviewNagCooldownDays: 5 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 202, title: "Hold cooldown", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#202", outcome: "completed" });
@@ -409,8 +409,8 @@ describe("queue processors", () => {
installation: { id: 123, account: { login: "JSONbored", id: 1, type: "User" }, target_type: "User", repository_selection: "all", permissions: { metadata: "read", pull_requests: "write", issues: "write" }, events: ["issue_comment"] },
repositories: [{ name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }],
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, autonomy: { close: "auto", label: "auto" } });
- await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagLabel: "too-chatty" } }, "repo_file");
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagLabel: "too-chatty" } }, "repo_file");
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 203, title: "Close cooldown", state: "open", user: { login: "chatty" }, head: { sha: "sha203" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#203", outcome: "completed" });
@@ -442,7 +442,8 @@ describe("queue processors", () => {
installation: { id: 123, account: { login: "JSONbored", id: 1, type: "User" }, target_type: "User", repository_selection: "all", permissions: { metadata: "read", pull_requests: "write", issues: "write" }, events: ["issue_comment"] },
repositories: [{ name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }],
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, autonomy: { close: "auto", label: "auto" } });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
// PR A: "chatty" already sent 3 pings (the full budget) and PR A was closed for it -- this is the exact
// state left behind by the "close policy on a PR thread" scenario above.
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 220, title: "PR A (already closed)", state: "closed", user: { login: "chatty" }, head: { sha: "sha220" }, author_association: "NONE", labels: [], body: "" });
@@ -479,7 +480,7 @@ describe("queue processors", () => {
it("close policy degrades to hold on an ISSUE thread (no closeIssue primitive yet)", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#204", outcome: "completed" });
}
@@ -503,7 +504,7 @@ describe("queue processors", () => {
it("never throttles an exempt login, even over threshold", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, autoCloseExemptLogins: ["chatty"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, autoCloseExemptLogins: ["chatty"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 205, title: "Exempt author", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 5; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#205", outcome: "completed" });
@@ -529,7 +530,7 @@ describe("queue processors", () => {
it("never throttles a third party pinging on someone else's PR — only the thread's OWN author is tracked", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 206, title: "Third party pinger", state: "open", user: { login: "pr-author" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubReviewNagFetch(206, seen);
@@ -558,7 +559,7 @@ describe("queue processors", () => {
installation: { id: 123, account: { login: "", id: 1, type: "User" }, target_type: "User", repository_selection: "all", permissions: { metadata: "read", pull_requests: "write", issues: "write" }, events: ["issue_comment"] },
repositories: [{ name: "noslash", full_name: "noslash", private: false, owner: { login: "" } }],
});
- await upsertRepositorySettings(env, { repoFullName: "noslash", reviewNagPolicy: "hold", reviewNagMaxPings: 3 });
+ await upsertRepoFocusManifest(env, "noslash", { settings: { reviewNagPolicy: "hold", reviewNagMaxPings: 3 } });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "noslash#209", outcome: "completed" });
}
@@ -586,7 +587,7 @@ describe("queue processors", () => {
it("never throttles the literal repo owner self-pinging their own PR", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 1 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 1 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 207, title: "Owner PR", state: "open", user: { login: "JSONbored" }, author_association: "OWNER", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubReviewNagFetch(207, seen);
@@ -611,7 +612,7 @@ describe("queue processors", () => {
it("never throttles an ADMIN_GITHUB_LOGINS fleet-operator, even over threshold", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), ADMIN_GITHUB_LOGINS: "fleet-admin" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 208, title: "Admin PR", state: "open", user: { login: "fleet-admin" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 5; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "fleet-admin", targetKey: "JSONbored/gittensory#208", outcome: "completed" });
@@ -637,7 +638,8 @@ describe("queue processors", () => {
it("hold policy respects agentDryRun — records a denied cooldown-applied audit and never posts the reply live (#2258 parity)", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "hold", reviewNagMaxPings: 3, agentDryRun: true });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", agentDryRun: true });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "hold", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 209, title: "Dry-run hold", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#209", outcome: "completed" });
@@ -663,7 +665,7 @@ describe("queue processors", () => {
it("close policy falls through harmlessly when the PR is no longer open by the time the threshold fires", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 210, title: "Already closed", state: "closed", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#210", outcome: "completed" });
@@ -689,7 +691,8 @@ describe("queue processors", () => {
it("close policy records a denied cooldown-applied audit when autonomy is not acting for label/close (empty plan)", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, autonomy: {} });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: {} });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 211, title: "Observe-only autonomy", state: "open", user: { login: "chatty" }, head: { sha: "sha211" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#211", outcome: "completed" });
@@ -716,7 +719,8 @@ describe("queue processors", () => {
it("close policy denies the mutation (never crashes) when no installation is on record — installationPermissions falls back to null", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, autonomy: { close: "auto", label: "auto" } });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto", label: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 212, title: "No installation row", state: "open", user: { login: "chatty" }, head: { sha: "sha212" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.review_nag_ping", actor: "chatty", targetKey: "JSONbored/gittensory#212", outcome: "completed" });
@@ -771,7 +775,7 @@ describe("queue processors", () => {
it("is off by default (no monitored logins configured) — no ping is tracked", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close" });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close" } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 300, title: "No monitored logins", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(300, seen);
@@ -793,7 +797,7 @@ describe("queue processors", () => {
it("detects a mention of a configured maintainer login and records a ping under threshold without acting", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 301, title: "Under threshold", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(301, seen);
@@ -816,7 +820,7 @@ describe("queue processors", () => {
it("REGRESSION: matches bot-shaped monitored logins literally", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["dependabot[bot]"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["dependabot[bot]"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 312, title: "Bot mention", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(312, seen);
@@ -838,7 +842,7 @@ describe("queue processors", () => {
it("REGRESSION: does not treat bot-login metacharacters as a regex character class", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMonitoredMentions: ["dependabot[bot]"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMonitoredMentions: ["dependabot[bot]"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 313, title: "Bot false positive", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(313, seen);
@@ -860,7 +864,7 @@ describe("queue processors", () => {
it("case-insensitively matches a monitored login and ignores an unrelated mention", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMonitoredMentions: ["JSONbored"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMonitoredMentions: ["JSONbored"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 302, title: "Case + unrelated", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(302, seen);
@@ -897,7 +901,7 @@ describe("queue processors", () => {
it("counts a monitored-login mention independently of the @loopover ping counter", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 303, title: "Independent counters", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(303, seen);
@@ -922,7 +926,7 @@ describe("queue processors", () => {
it("hold policy: posts a cooldown reply naming the mentioned login and short-circuits once the threshold is crossed", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "hold", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "hold", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 304, title: "Hold on mention", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.monitored_mention_ping", actor: "chatty", targetKey: "JSONbored/gittensory#304#mention:jsonbored", outcome: "completed" });
@@ -952,7 +956,8 @@ describe("queue processors", () => {
installation: { id: 123, account: { login: "JSONbored", id: 1, type: "User" }, target_type: "User", repository_selection: "all", permissions: { metadata: "read", pull_requests: "write", issues: "write" }, events: ["issue_comment"] },
repositories: [{ name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }],
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"], reviewNagLabel: "too-chatty", autonomy: { close: "auto" } });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"], reviewNagLabel: "too-chatty" } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 305, title: "Close on mention", state: "open", user: { login: "chatty" }, head: { sha: "sha305" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.monitored_mention_ping", actor: "chatty", targetKey: "JSONbored/gittensory#305#mention:jsonbored", outcome: "completed" });
@@ -982,7 +987,8 @@ describe("queue processors", () => {
installation: { id: 123, account: { login: "JSONbored", id: 1, type: "User" }, target_type: "User", repository_selection: "all", permissions: { metadata: "read", pull_requests: "write", issues: "write" }, events: ["issue_comment"] },
repositories: [{ name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }],
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"], autonomy: { close: "auto" } });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored"] } });
// PR A: "chatty" already sent 3 pings mentioning @JSONbored (the full budget) and PR A was closed for it.
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 320, title: "PR A (already closed)", state: "closed", user: { login: "chatty" }, head: { sha: "sha320" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
@@ -1019,13 +1025,8 @@ describe("queue processors", () => {
installation: { id: 123, account: { login: "JSONbored", id: 1, type: "User" }, target_type: "User", repository_selection: "all", permissions: { metadata: "read", pull_requests: "write", issues: "write" }, events: ["issue_comment"] },
repositories: [{ name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }],
});
- await upsertRepositorySettings(env, {
- repoFullName: "JSONbored/gittensory",
- reviewNagPolicy: "close",
- reviewNagMaxPings: 3,
- reviewNagMonitoredMentions: ["JSONbored", "other-maintainer"],
- autonomy: { close: "auto" },
- });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", autonomy: { close: "auto" } });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 3, reviewNagMonitoredMentions: ["JSONbored", "other-maintainer"] } });
// PR A: "chatty" already exhausted the @JSONbored budget (3 pings) -- same seed as the carryover test above.
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 322, title: "PR A (JSONbored exhausted)", state: "closed", user: { login: "chatty" }, head: { sha: "sha322" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
@@ -1058,7 +1059,7 @@ describe("queue processors", () => {
it("does NOT throttle the repo owner, an admin login, an automation bot, or an exempt login", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), ADMIN_GITHUB_LOGINS: "fleet-admin" });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 1, reviewNagMonitoredMentions: ["JSONbored"], autoCloseExemptLogins: ["trusted-regular"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 1, reviewNagMonitoredMentions: ["JSONbored"], autoCloseExemptLogins: ["trusted-regular"] } });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(306, seen);
for (const [commenter, prNumber] of [
@@ -1087,7 +1088,7 @@ describe("queue processors", () => {
it("does NOT throttle a third party mentioning the login on someone else's thread (thread-author-only scope)", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMonitoredMentions: ["JSONbored"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMonitoredMentions: ["JSONbored"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 310, title: "Third party", state: "open", user: { login: "thread-author" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(310, seen);
@@ -1109,7 +1110,7 @@ describe("queue processors", () => {
it("REGRESSION: a redelivered webhook (same deliveryId) does not double-count the ping", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", reviewNagPolicy: "close", reviewNagMaxPings: 5, reviewNagMonitoredMentions: ["JSONbored"] });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { reviewNagPolicy: "close", reviewNagMaxPings: 5, reviewNagMonitoredMentions: ["JSONbored"] } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 311, title: "Redelivery", state: "open", user: { login: "chatty" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[], labels: [] as string[], closed: false };
stubMonitoredMentionFetch(311, seen);
@@ -1171,7 +1172,7 @@ describe("queue processors", () => {
it("records invocations under the configured threshold without holding — the normal reply still proceeds", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 5, commandRateLimitWindowHours: 24 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 301, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
stubCommandRateLimitFetch(301, seen);
@@ -1185,7 +1186,7 @@ describe("queue processors", () => {
it("hold policy: posts a cooldown reply and short-circuits once a CHEAP command crosses its threshold", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 3, commandRateLimitWindowHours: 24 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 3, commandRateLimitWindowHours: 24 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 302, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 3; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.command_invocation", actor: "maintainer", targetKey: "JSONbored/gittensory#302#help", outcome: "completed" });
@@ -1204,7 +1205,7 @@ describe("queue processors", () => {
it("an AI-cost-bearing command uses the TIGHTER commandRateLimitAiMaxPerWindow default, not the cheap-command limit", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
// Cheap-command limit left generous (20, the default); only the AI limit is tight enough to trip here.
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 2, commandRateLimitWindowHours: 24 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 2, commandRateLimitWindowHours: 24 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 303, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
for (let i = 0; i < 2; i += 1) {
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.command_invocation", actor: "maintainer", targetKey: "JSONbored/gittensory#303#next-action", outcome: "completed" });
@@ -1220,7 +1221,7 @@ describe("queue processors", () => {
it("commands have INDEPENDENT counters — repeatedly invoking one command never throttles a DIFFERENT command", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 304, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
// Already at the "help" limit (1) — a further "help" invocation would be held.
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.command_invocation", actor: "maintainer", targetKey: "JSONbored/gittensory#304#help", outcome: "completed" });
@@ -1239,7 +1240,7 @@ describe("queue processors", () => {
// actor who exhausted the limit on one thread could get a full new budget simply by invoking the SAME
// command on a DIFFERENT issue/PR number — the exact bypass class #4021 already fixed for review-nag.
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 340, title: "Rate limit target A", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 341, title: "Rate limit target B (fresh)", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
// Already at the "help" limit (1) on issue #340.
@@ -1257,7 +1258,8 @@ describe("queue processors", () => {
it("dry-run mode: holds the command but never posts a live cooldown comment", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24, agentDryRun: true });
+ await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", agentDryRun: true });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 305, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
await repositoriesModule.recordAuditEvent(env, { eventType: "github_app.command_invocation", actor: "maintainer", targetKey: "JSONbored/gittensory#305#help", outcome: "completed" });
const seen = { comments: [] as string[] };
@@ -1273,7 +1275,7 @@ describe("queue processors", () => {
// second delivery would increment the counter again for what is really ONE real invocation, and could
// incorrectly cross the rate-limit threshold on a redelivery alone.
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24 });
+ await upsertRepoFocusManifest(env, "JSONbored/gittensory", { settings: { commandRateLimitPolicy: "hold", commandRateLimitMaxPerWindow: 1, commandRateLimitWindowHours: 24 } });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 306, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
stubCommandRateLimitFetch(306, seen);
@@ -1628,7 +1630,6 @@ describe("queue processors", () => {
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
AI_ADVISORY: { run: async () => ({ response: '{"command": "blockers"}' }) } as unknown as Ai,
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 309, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
// advisoryAiRouting is config-as-code only -- enable intentRouting the real way, through the repo's
@@ -1637,7 +1638,7 @@ describe("queue processors", () => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 5\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "maintain" });
@@ -1661,14 +1662,13 @@ describe("queue processors", () => {
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
AI_ADVISORY: { run: async () => ({ response: '{"command": "ask"}' }) } as unknown as Ai,
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 315, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 5\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "maintain" });
@@ -1710,14 +1710,13 @@ describe("queue processors", () => {
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
AI_ADVISORY: { run: advisoryRun } as unknown as Ai,
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 316, title: "Unauthorized intent routing", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 5\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "none" });
@@ -1744,14 +1743,13 @@ describe("queue processors", () => {
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
AI_ADVISORY: { run: async () => ({ response: '{"command": null}' }) } as unknown as Ai,
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 311, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 5\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "maintain" });
@@ -1775,14 +1773,13 @@ describe("queue processors", () => {
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
AI_ADVISORY: { run: async () => ({ response: '{"command": "blockers"}' }) } as unknown as Ai,
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 312, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 5\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "maintain" });
@@ -1803,7 +1800,6 @@ describe("queue processors", () => {
it("#4596: hold policy throttles intent-routing once the AI ceiling is crossed and skips the classifier (fails open to the existing did-you-mean hint)", async () => {
const advisoryRun = vi.fn(async () => ({ response: '{"command": "blockers"}' }));
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), AI_ADVISORY: { run: advisoryRun } as unknown as Ai });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 1, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 313, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
// Already at the ceiling (1) -- the next invocation must be held before it ever reaches the classifier.
await repositoriesModule.recordAuditEvent(env, {
@@ -1817,7 +1813,7 @@ describe("queue processors", () => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 1\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "maintain" });
@@ -1840,7 +1836,6 @@ describe("queue processors", () => {
// classifier's own independent counter.
const advisoryRun = vi.fn(async () => ({ response: '{"command": "blockers"}' }));
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), AI_ADVISORY: { run: advisoryRun } as unknown as Ai });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 1, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 342, title: "Rate limit target A", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 343, title: "Rate limit target B (fresh)", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
// Already at the ceiling (1) on issue #342.
@@ -1855,7 +1850,7 @@ describe("queue processors", () => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 1\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "maintain" });
@@ -1877,14 +1872,13 @@ describe("queue processors", () => {
it("#4596: REGRESSION: a redelivered webhook does not re-classify or double-count the intent-routing invocation", async () => {
const advisoryRun = vi.fn(async () => ({ response: '{"command": "blockers"}' }));
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), AI_ADVISORY: { run: advisoryRun } as unknown as Ai });
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 314, title: "Rate limit target", state: "open", user: { login: "oktofeesh1" }, author_association: "NONE", labels: [], body: "" });
const seen = { comments: [] as string[] };
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 5\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
if (url.includes("/collaborators/") && url.includes("/permission")) return Response.json({ permission: "maintain" });
@@ -1937,7 +1931,6 @@ describe("queue processors", () => {
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
AI_ADVISORY: { run: async () => ({ response: '{"command": "blockers"}' }) } as unknown as Ai,
});
- await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory", commandRateLimitPolicy: "hold", commandRateLimitAiMaxPerWindow: 5, commandRateLimitWindowHours: 24 });
await upsertPullRequestFromGitHub(env, "JSONbored/gittensory", { number: 319, title: "Rate limit target", state: "open", user: { login: "own-pr-miner" }, author_association: "NONE", labels: [], body: "" });
await upsertOfficialMinerDetection(env, "own-pr-miner", { status: "confirmed", snapshot: queueMinerSnapshot("own-pr-miner") }, 60_000);
const seen = { comments: [] as string[] };
@@ -1945,7 +1938,7 @@ describe("queue processors", () => {
const url = input.toString();
const method = init?.method ?? "GET";
if (url.includes("raw.githubusercontent.com") && url.includes(".loopover.yml")) {
- return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n", { status: 200 });
+ return new Response("settings:\n advisoryAiRouting:\n intentRouting: true\n commandRateLimitPolicy: hold\n commandRateLimitAiMaxPerWindow: 5\n commandRateLimitWindowHours: 24\n", { status: 200 });
}
if (url.includes("/access_tokens")) return Response.json({ token: "fake-installation-token" });
// No repo permission at all -- the ONLY route to authorization is the confirmed_miner role on their own PR.
diff --git a/test/unit/queue-lifecycle-guards.test.ts b/test/unit/queue-lifecycle-guards.test.ts
index f89c1ea6cd..983228c9ab 100644
--- a/test/unit/queue-lifecycle-guards.test.ts
+++ b/test/unit/queue-lifecycle-guards.test.ts
@@ -1928,11 +1928,12 @@ describe("review-evasion protection (#review-evasion-protection)", () => {
},
repositories: [{ name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }],
});
- // reviewEvasionProtection/reviewEvasionComment are manifest-only now (Batch B, loopover#6443); pull any
- // test override out of `overrides` before it reaches the DB write below so a per-test
- // `{ reviewEvasionProtection: "off" }`/`{ reviewEvasionComment: false }` still takes effect via the
- // manifest overlay instead of being silently outranked by the hardcoded default.
- const { reviewEvasionProtection, reviewEvasionComment, ...dbOverrides } = overrides;
+ // reviewEvasionProtection/reviewEvasionComment (Batch B, loopover#6443) and autoCloseExemptLogins
+ // (loopover#6445) are manifest-only now; pull any test override out of `overrides` before it reaches
+ // the DB write below so a per-test `{ reviewEvasionProtection: "off" }`/`{ reviewEvasionComment: false }`/
+ // `{ autoCloseExemptLogins: [...] }` still takes effect via the manifest overlay instead of being
+ // silently outranked by the hardcoded default.
+ const { reviewEvasionProtection, reviewEvasionComment, autoCloseExemptLogins, ...dbOverrides } = overrides;
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
autonomy: { close: "auto" },
@@ -1946,6 +1947,7 @@ describe("review-evasion protection (#review-evasion-protection)", () => {
checkRunMode: "off",
reviewEvasionProtection: (reviewEvasionProtection as "off" | "close" | undefined) ?? "close",
...(reviewEvasionComment !== undefined ? { reviewEvasionComment: reviewEvasionComment as boolean } : {}),
+ ...(autoCloseExemptLogins !== undefined ? { autoCloseExemptLogins: autoCloseExemptLogins as string[] } : {}),
},
});
}
diff --git a/test/unit/repository-settings-project-milestone-match.test.ts b/test/unit/repository-settings-project-milestone-match.test.ts
index 29e1261d14..d5947695b2 100644
--- a/test/unit/repository-settings-project-milestone-match.test.ts
+++ b/test/unit/repository-settings-project-milestone-match.test.ts
@@ -1,76 +1,67 @@
import { describe, expect, it } from "vitest";
import { getRepositorySettings, upsertRepositorySettings } from "../../src/db/repositories";
+import { resolveRepositorySettings } from "../../src/settings/repository-settings";
+import { upsertRepoFocusManifest } from "../../src/signals/focus-manifest-loader";
import { createTestEnv } from "../helpers/d1";
-// #3183: autoProjectMilestoneMatch is the tri-state config for auto-project/milestone matching, mirroring the
-// reviewCheckMode template (#2852). "off" is the conservative, opt-in default; "suggest" and "auto" currently
-// behave identically (post a comment) since real milestone attachment isn't wired until #3185.
-describe("repository_settings: autoProjectMilestoneMatch default + round-trip (#3183)", () => {
+// #3183/loopover#6445: autoProjectMilestoneMatch is the tri-state config for auto-project/milestone matching,
+// mirroring the reviewCheckMode template (#2852). "off" is the conservative, opt-in default; "suggest" and
+// "auto" currently behave identically (post a comment) since real milestone attachment isn't wired until
+// #3185. Moved off the DB entirely (config-as-code migration, loopover#6445) -- config-as-code only via
+// .loopover.yml's settings: block now.
+describe("repository_settings: autoProjectMilestoneMatch config-as-code (#3183)", () => {
it("getRepositorySettings returns off for a repo with no DB row at all (conservative, opt-in default)", async () => {
const env = createTestEnv();
const settings = await getRepositorySettings(env, "acme/brand-new-repo");
expect(settings.autoProjectMilestoneMatch).toBe("off");
});
- it("upsertRepositorySettings persists off when the caller omits the field entirely", async () => {
+ it("getRepositorySettings ignores a caller-supplied override on upsert -- always off (no DB column to persist it in)", async () => {
const env = createTestEnv();
- await upsertRepositorySettings(env, { repoFullName: "acme/omits-field" });
- const settings = await getRepositorySettings(env, "acme/omits-field");
+ await upsertRepositorySettings(env, { repoFullName: "acme/round-trip", autoProjectMilestoneMatch: "suggest" });
+ const settings = await getRepositorySettings(env, "acme/round-trip");
expect(settings.autoProjectMilestoneMatch).toBe("off");
});
- it("an explicit suggest/auto opt-in round-trips through a re-upsert that carries it forward explicitly", async () => {
+ it("resolveRepositorySettings honors an explicit suggest/auto opt-in from .loopover.yml's settings: block", async () => {
const env = createTestEnv();
- await upsertRepositorySettings(env, { repoFullName: "acme/round-trip", autoProjectMilestoneMatch: "suggest" });
- const settings = await getRepositorySettings(env, "acme/round-trip");
+ await upsertRepositorySettings(env, { repoFullName: "acme/round-trip" });
+ await upsertRepoFocusManifest(env, "acme/round-trip", { settings: { autoProjectMilestoneMatch: "suggest" } });
+ const settings = await resolveRepositorySettings(env, "acme/round-trip");
expect(settings.autoProjectMilestoneMatch).toBe("suggest");
- // A true read-modify-write caller (the route-handler pattern: spread current settings, then override) must
- // carry the persisted value forward explicitly -- upsertRepositorySettings never merges against the DB row.
- await upsertRepositorySettings(env, { ...settings, repoFullName: "acme/round-trip" });
- const after = await getRepositorySettings(env, "acme/round-trip");
- expect(after.autoProjectMilestoneMatch).toBe("suggest");
});
- it("auto round-trips distinctly from suggest", async () => {
+ it("auto resolves distinctly from suggest via the manifest overlay", async () => {
const env = createTestEnv();
- await upsertRepositorySettings(env, { repoFullName: "acme/auto-mode", autoProjectMilestoneMatch: "auto" });
- const settings = await getRepositorySettings(env, "acme/auto-mode");
+ await upsertRepositorySettings(env, { repoFullName: "acme/auto-mode" });
+ await upsertRepoFocusManifest(env, "acme/auto-mode", { settings: { autoProjectMilestoneMatch: "auto" } });
+ const settings = await resolveRepositorySettings(env, "acme/auto-mode");
expect(settings.autoProjectMilestoneMatch).toBe("auto");
});
-
- it("an invalid persisted DB value fails closed to off on read", async () => {
- const env = createTestEnv();
- await upsertRepositorySettings(env, { repoFullName: "acme/malformed" });
- await env.DB.prepare("UPDATE repository_settings SET project_milestone_match_mode = ? WHERE repo_full_name = ?").bind("sometimes", "acme/malformed").run();
- const settings = await getRepositorySettings(env, "acme/malformed");
- expect(settings.autoProjectMilestoneMatch).toBe("off");
- });
});
-// #3186: autoProjectMilestoneMatchBackend selects which tracker the match/attach logic queries -- "github"
-// (Milestones + Projects v2, the conservative default) or "linear" (an opted-in per-repo API key).
-describe("repository_settings: autoProjectMilestoneMatchBackend default + round-trip (#3186)", () => {
+// #3186/loopover#6445: autoProjectMilestoneMatchBackend selects which tracker the match/attach logic queries --
+// "github" (Milestones + Projects v2, the conservative default) or "linear" (an opted-in per-repo API key).
+// Moved off the DB entirely (config-as-code migration, loopover#6445).
+describe("repository_settings: autoProjectMilestoneMatchBackend config-as-code (#3186)", () => {
it("getRepositorySettings returns github for a repo with no DB row at all", async () => {
const env = createTestEnv();
const settings = await getRepositorySettings(env, "acme/brand-new-repo");
expect(settings.autoProjectMilestoneMatchBackend).toBe("github");
});
- it("an explicit linear opt-in round-trips through a re-upsert that carries it forward explicitly", async () => {
+ it("getRepositorySettings ignores a caller-supplied override on upsert -- always github (no DB column to persist it in)", async () => {
const env = createTestEnv();
await upsertRepositorySettings(env, { repoFullName: "acme/linear-backend", autoProjectMilestoneMatchBackend: "linear" });
const settings = await getRepositorySettings(env, "acme/linear-backend");
- expect(settings.autoProjectMilestoneMatchBackend).toBe("linear");
- await upsertRepositorySettings(env, { ...settings, repoFullName: "acme/linear-backend" });
- const after = await getRepositorySettings(env, "acme/linear-backend");
- expect(after.autoProjectMilestoneMatchBackend).toBe("linear");
+ expect(settings.autoProjectMilestoneMatchBackend).toBe("github");
});
- it("an invalid persisted DB value fails closed to github on read", async () => {
+ it("resolveRepositorySettings honors an explicit linear opt-in from .loopover.yml's settings: block", async () => {
const env = createTestEnv();
- await upsertRepositorySettings(env, { repoFullName: "acme/malformed-backend" });
- await env.DB.prepare("UPDATE repository_settings SET auto_project_milestone_match_backend = ? WHERE repo_full_name = ?").bind("jira", "acme/malformed-backend").run();
- const settings = await getRepositorySettings(env, "acme/malformed-backend");
- expect(settings.autoProjectMilestoneMatchBackend).toBe("github");
+ await upsertRepositorySettings(env, { repoFullName: "acme/linear-backend" });
+ await upsertRepoFocusManifest(env, "acme/linear-backend", { settings: { autoProjectMilestoneMatchBackend: "linear" } });
+ const settings = await resolveRepositorySettings(env, "acme/linear-backend");
+ expect(settings.autoProjectMilestoneMatchBackend).toBe("linear");
});
});