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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@
import { buildPredictedGateVerdict } from "../rules/predicted-gate";
import { buildMaintainerActivationPreview, recommendedAdvisoryActivationSettings } from "../services/maintainer-activation";
import { buildRepoOutcomeCalibration } from "../services/outcome-calibration";
import { loadGatePrecisionReport } from "../services/gate-precision";

Check notice on line 221 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
import { buildMaintainerQualityDashboard, isMaintainerQualityDataStale } from "../services/maintainer-quality-dashboard";
import { MAX_LOCAL_SCORER_WARNING_CHARS, MAX_LOCAL_SCORER_WARNING_COUNT } from "../signals/local-scorer-diagnostics";
import { compileFocusManifestPolicy, MAX_FOCUS_MANIFEST_BYTES } from "../signals/focus-manifest";
import { resolveRepositorySettings } from "../settings/repository-settings";
import { loadPublicRepoFocusManifest, loadRepoFocusManifest, upsertRepoFocusManifest } from "../signals/focus-manifest-loader";
import { buildRepoOnboardingPackPreviewForRepo } from "../services/repo-onboarding-pack";
import { generateContributorIssueDrafts } from "../services/contributor-issue-draft";
Expand Down Expand Up @@ -2195,7 +2196,7 @@
if (unauthorized) return unauthorized;
}
const [settings, issues, pullRequests] = await Promise.all([
getRepositorySettings(c.env, fullName),
resolveRepositorySettings(c.env, fullName),
listIssues(c.env, fullName),
listPullRequests(c.env, fullName),
]);
Expand Down
13 changes: 2 additions & 11 deletions src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,15 @@
buildRoleContext,
detectGittensorContributor,
PR_PANEL_RETRIGGER_MARKER,
unionScopedOverlapClusters,

Check notice on line 143 in src/queue/processors.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
type ContributorProfile,
} from "../signals/engine";
import { buildIssueSlopAssessment, buildSlopAssessment, type SlopBand } from "../signals/slop";
import { runGittensoryAiSlopAdvisory } from "../services/ai-slop";
import { decidePublicSurface } from "../signals/settings-preview";
import { buildFocusManifestGuidance } from "../signals/focus-manifest";
import { loadRepoFocusManifest } from "../signals/focus-manifest-loader";
import { buildFocusManifestGuidance, resolveEffectiveSettings } from "../signals/focus-manifest";
import { resolveRepositorySettings } from "../settings/repository-settings";
import type { LocalBranchAnalysisInput } from "../signals/local-branch";
import { runGittensoryAiReview } from "../services/ai-review";
import type { AdvisoryFinding, ContributorEvidenceRecord, ContributorRepoStatRecord, DetectedNotificationEvent, GitHubWebhookPayload, IssueRecord, JobMessage, JsonValue, PullRequestFilePathRecord, PullRequestRecord, RepositoryRecord, RepositorySettings } from "../types";
Expand Down Expand Up @@ -1114,16 +1115,6 @@
}
}

/**
* Effective repository settings for webhook handling: the DB-backed settings overlaid with the repo's
* `.gittensory.yml` (config-as-code). This single resolver is why EVERYTHING — gate on/off, all blocker
* modes, comments, labels, surface, audience — is controllable from the repo's config file.
*/
async function resolveRepositorySettings(env: Env, repoFullName: string): Promise<RepositorySettings> {
const [dbSettings, manifest] = await Promise.all([getRepositorySettings(env, repoFullName), loadRepoFocusManifest(env, repoFullName)]);
return resolveEffectiveSettings(dbSettings, manifest);
}

/** Build a bounded unified-diff string from cached PR files for the AI reviewer. Caps total size so a
* huge PR cannot blow the model context or the neuron budget; each file's patch is taken from the raw
* GitHub file payload when present. */
Expand Down
2 changes: 1 addition & 1 deletion src/services/maintainer-activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@
const recent = [...args.pullRequests].sort((left, right) => recencyKey(right).localeCompare(recencyKey(left))).slice(0, sampleSize);

const codeCounts = new Map<string, number>();
const samples: MaintainerActivationSample[] = recent.map((pr) => {

Check notice on line 58 in src/services/maintainer-activation.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
const advisory = buildPullRequestAdvisory(args.repo, pr, {
otherOpenPullRequests: args.pullRequests.filter((other) => other.number !== pr.number),
requireLinkedIssue: true,
requireLinkedIssue: args.settings.requireLinkedIssue || args.settings.linkedIssueGateMode !== "off",
});
for (const finding of advisory.findings) codeCounts.set(finding.code, (codeCounts.get(finding.code) ?? 0) + 1);
return {
Expand Down
13 changes: 13 additions & 0 deletions src/settings/repository-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getRepositorySettings } from "../db/repositories";

Check notice on line 1 in src/settings/repository-settings.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
import { resolveEffectiveSettings } from "../signals/focus-manifest";
import { loadRepoFocusManifest } from "../signals/focus-manifest-loader";
import type { RepositorySettings } from "../types";

/** Effective repository settings: DB values overlaid with `.gittensory.yml` (config-as-code). */
export async function resolveRepositorySettings(env: Env, repoFullName: string): Promise<RepositorySettings> {
const [dbSettings, manifest] = await Promise.all([
getRepositorySettings(env, repoFullName),
loadRepoFocusManifest(env, repoFullName),
]);
return resolveEffectiveSettings(dbSettings, manifest);
}
5 changes: 5 additions & 0 deletions src/signals/focus-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,9 +524,14 @@
if (gate.aiReviewByok !== null) effective.aiReviewByok = gate.aiReviewByok;
if (gate.aiReviewProvider !== null) effective.aiReviewProvider = gate.aiReviewProvider;
if (gate.aiReviewModel !== null) effective.aiReviewModel = gate.aiReviewModel;
if (gate.mergeReadiness !== null) effective.mergeReadinessGateMode = gate.mergeReadiness;

Check notice on line 527 in src/signals/focus-manifest.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
if (gate.manifestPolicy !== null) effective.manifestPolicyGateMode = gate.manifestPolicy;
if (gate.firstTimeContributorGrace !== null) effective.firstTimeContributorGrace = gate.firstTimeContributorGrace;
// The dashboard "Require linked issue" toggle must not silently diverge from gate blocking: when the
// boolean is on but linkedIssueGateMode is still off, treat it as a block requirement (#797).
if (effective.requireLinkedIssue && effective.linkedIssueGateMode === "off") {
effective.linkedIssueGateMode = "block";
}
return effective;
}

Expand Down
8 changes: 8 additions & 0 deletions src/signals/settings-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,13 @@
autoLabelEnabled: boolean;
gittensorLabel: string;
createMissingLabel: boolean;
includeMaintainerAuthors: boolean;

Check notice on line 198 in src/signals/settings-preview.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
requireLinkedIssue: boolean;
badgeEnabled: boolean;
aiReviewMode: RepositorySettings["aiReviewMode"];
aiReviewByok: boolean;
aiReviewProvider: string | null;
aiReviewModel: string | null;
commandAuthorization: {
defaultAllowed: CommandAuthorizationRole[];
commandOverrides: Array<{ command: string; allowedRoles: CommandAuthorizationRole[] }>;
Expand Down Expand Up @@ -316,6 +320,10 @@
includeMaintainerAuthors: settings.includeMaintainerAuthors,
requireLinkedIssue: settings.requireLinkedIssue,
badgeEnabled: settings.badgeEnabled ?? false,
aiReviewMode: settings.aiReviewMode,
aiReviewByok: settings.aiReviewByok,
aiReviewProvider: settings.aiReviewProvider ?? null,
aiReviewModel: settings.aiReviewModel ?? null,
commandAuthorization: summarizeCommandAuthorizationPolicy(settings.commandAuthorization),
},
commandAuthorizationPreview,
Expand Down
8 changes: 8 additions & 0 deletions test/unit/focus-manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -948,9 +948,17 @@
const db = { aiReviewMode: "off", aiReviewByok: false } as unknown as RepositorySettings;
// settings: applies first, then the friendly gate.aiReview alias wins for its fields.
const eff = resolveEffectiveSettings(db, parseFocusManifest({ settings: { aiReviewMode: "advisory" }, gate: { aiReview: { mode: "block", byok: true } } }));
expect(eff.aiReviewMode).toBe("block");

Check notice on line 951 in test/unit/focus-manifest.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
expect(eff.aiReviewByok).toBe(true);
});

it("promotes requireLinkedIssue to linkedIssueGateMode block when the gate mode is still off (#797)", () => {
const eff = resolveEffectiveSettings(
{ requireLinkedIssue: true, linkedIssueGateMode: "off" } as RepositorySettings,
parseFocusManifest(null),
);
expect(eff.linkedIssueGateMode).toBe("block");
});
});

describe("parseFocusManifest review config", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/unit/maintainer-activation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@

describe("buildMaintainerActivationPreview", () => {
it("summarizes advisory findings across recent PRs and recommends advisory enable when the gate is off", () => {
const preview = buildMaintainerActivationPreview({

Check notice on line 72 in test/unit/maintainer-activation.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
repoFullName: repo.fullName,
repo,
settings: settings(),
settings: settings({ requireLinkedIssue: true }),
pullRequests: [pr(1, { linkedIssues: [] }), pr(2, { linkedIssues: [5] })],
generatedAt: "2026-06-14T00:00:00.000Z",
});
Expand Down
138 changes: 138 additions & 0 deletions test/unit/repository-settings-enforcement.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { describe, expect, it } from "vitest";

Check notice on line 1 in test/unit/repository-settings-enforcement.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.
import { gateCheckPolicy } from "../../src/queue/processors";
import { evaluateGateCheck } from "../../src/rules/advisory";
import { runAiReviewForAdvisory } from "../../src/queue/processors";
import { buildMaintainerActivationPreview } from "../../src/services/maintainer-activation";
import { decidePublicSurface } from "../../src/signals/settings-preview";
import { parseFocusManifest, resolveEffectiveSettings } from "../../src/signals/focus-manifest";
import type { Advisory, PullRequestRecord, RepositoryRecord, RepositorySettings } from "../../src/types";

function settings(over: Partial<RepositorySettings> = {}): RepositorySettings {
return {
repoFullName: "owner/repo",
commentMode: "detected_contributors_only",
publicAudienceMode: "oss_maintainer",
publicSignalLevel: "standard",
checkRunMode: "off",
checkRunDetailLevel: "standard",
gateCheckMode: "enabled",
gatePack: "gittensor",
linkedIssueGateMode: "off",
duplicatePrGateMode: "advisory",
qualityGateMode: "advisory",
slopGateMode: "off",
mergeReadinessGateMode: "off",
manifestPolicyGateMode: "off",
firstTimeContributorGrace: false,
slopAiAdvisory: false,
qualityGateMinScore: null,
autoLabelEnabled: true,
gittensorLabel: "gittensor",
createMissingLabel: true,
publicSurface: "comment_and_label",
includeMaintainerAuthors: false,
requireLinkedIssue: false,
backfillEnabled: true,
privateTrustEnabled: true,
aiReviewMode: "off",
aiReviewByok: false,
aiReviewProvider: null,
aiReviewModel: null,
...over,
};
}

function missingIssueAdvisory(): Advisory {
return {
id: "advisory-policy",
targetType: "pull_request",
targetKey: "owner/repo#7",
repoFullName: "owner/repo",
pullNumber: 7,
headSha: "sha7",
conclusion: "neutral",
severity: "warning",
title: "Gittensory advisory available",
summary: "1 advisory finding generated.",
findings: [{ code: "missing_linked_issue", title: "No linked issue detected", severity: "warning", detail: "No closing reference.", action: "Link the issue." }],
generatedAt: "2026-06-13T00:00:00.000Z",
};
}

describe("repository settings enforcement audit (#797)", () => {
it("maps requireLinkedIssue to linkedIssueGateMode block when the gate mode is off", () => {
const effective = resolveEffectiveSettings(settings({ requireLinkedIssue: true, linkedIssueGateMode: "off" }), parseFocusManifest(null));
expect(effective.linkedIssueGateMode).toBe("block");
});

it("blocks confirmed contributors when requireLinkedIssue is enabled via the boolean alone", () => {
const effective = resolveEffectiveSettings(settings({ requireLinkedIssue: true, linkedIssueGateMode: "off" }), parseFocusManifest(null));
const result = evaluateGateCheck(missingIssueAdvisory(), gateCheckPolicy(effective, null, true));
expect(result.conclusion).toBe("failure");
});

it("skips maintainer-authored public surfaces when includeMaintainerAuthors is false", () => {
const decision = decidePublicSurface({
settings: settings({ includeMaintainerAuthors: false }),
authorLogin: "owner",
authorType: "User",
authorAssociation: "OWNER",
minerStatus: "confirmed",
});
expect(decision).toMatchObject({ skipped: true, skipReason: "maintainer_author" });
});

it("no-ops AI review when aiReviewMode is off", async () => {
const advisory = missingIssueAdvisory();
const notes = await runAiReviewForAdvisory({} as Env, {
settings: settings({ aiReviewMode: "off" }),
advisory,
repoFullName: "owner/repo",
pr: { number: 7, title: "Test" },
author: "miner",
confirmedContributor: true,
});
expect(notes).toBeUndefined();
expect(advisory.findings.some((finding) => finding.code === "ai_consensus_defect")).toBe(false);
});

it("uses the repo requireLinkedIssue setting in maintainer activation previews", () => {
const repo: RepositoryRecord = {
fullName: "owner/repo",
owner: "owner",
name: "repo",
isInstalled: true,
isRegistered: true,
isPrivate: false,
registryConfig: { repo: "owner/repo", emissionShare: 0.01, issueDiscoveryShare: 0, maintainerCut: 0, labelMultipliers: {}, raw: {} },
};
const pull: PullRequestRecord = {
repoFullName: repo.fullName,
number: 1,
title: "No issue link",
state: "open",
authorLogin: "miner",
body: "No closing reference.",
labels: [],
linkedIssues: [],
createdAt: "2026-06-01T00:00:00.000Z",
updatedAt: "2026-06-01T00:00:00.000Z",
};
const withRequirement = buildMaintainerActivationPreview({
repoFullName: repo.fullName,
repo,
settings: settings({ requireLinkedIssue: true }),
pullRequests: [pull],
generatedAt: "2026-06-01T00:00:00.000Z",
});
const withoutRequirement = buildMaintainerActivationPreview({
repoFullName: repo.fullName,
repo,
settings: settings({ requireLinkedIssue: false, linkedIssueGateMode: "off" }),
pullRequests: [pull],
generatedAt: "2026-06-01T00:00:00.000Z",
});
expect(withRequirement.samples[0]?.findings.some((finding) => finding.code === "missing_linked_issue")).toBe(true);
expect(withoutRequirement.samples[0]?.findings.some((finding) => finding.code === "missing_linked_issue")).toBe(false);
});
});
Loading