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
6 changes: 3 additions & 3 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ import { loadGatePrecisionReport } from "../services/gate-precision";
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 } from "../signals/focus-manifest";
import { loadRepoFocusManifest, upsertRepoFocusManifest } from "../signals/focus-manifest-loader";
import { loadPublicRepoFocusManifest, loadRepoFocusManifest, upsertRepoFocusManifest } from "../signals/focus-manifest-loader";
import { buildRepoOnboardingPackPreviewForRepo } from "../services/repo-onboarding-pack";
import { generateContributorIssueDrafts } from "../services/contributor-issue-draft";
import { buildRepoSettingsPreview, type PublicSurfaceSkipReason } from "../signals/settings-preview";
Expand Down Expand Up @@ -2471,7 +2471,7 @@ export function createApp() {
listBountiesByRepo(c.env, parsed.data.repoFullName),
getOrCreateScoringModelSnapshot(c.env),
loadOrComputeIssueQualityResponse(c.env, parsed.data.repoFullName),
loadRepoFocusManifest(c.env, parsed.data.repoFullName),
loadPublicRepoFocusManifest(c.env, parsed.data.repoFullName),
]);
const fit = buildContributorFit(context.profile, context.repositories, [], [], context.syncStates, context.repoStats);
const scoringProfile = buildContributorScoringProfile({ login: parsed.data.login, fit, scoringSnapshot: snapshot });
Expand Down Expand Up @@ -2546,7 +2546,7 @@ export function createApp() {
listBountiesByRepo(c.env, parsed.data.repoFullName),
getOrCreateScoringModelSnapshot(c.env),
loadOrComputeIssueQualityResponse(c.env, parsed.data.repoFullName),
loadRepoFocusManifest(c.env, parsed.data.repoFullName),
loadPublicRepoFocusManifest(c.env, parsed.data.repoFullName),
]);
const fit = buildContributorFit(context.profile, context.repositories, [], [], context.syncStates, context.repoStats);
const scoringProfile = buildContributorScoringProfile({ login: parsed.data.login, fit, scoringSnapshot: snapshot });
Expand Down
27 changes: 25 additions & 2 deletions src/signals/focus-manifest-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,33 @@ export async function loadRepoFocusManifest(
env: Env,
repoFullName: string,
options: { fetcher?: RepoFocusManifestFetcher; maxAgeMs?: number; refresh?: boolean } = {},
): Promise<FocusManifest> {
return loadRepoFocusManifestWithCachePolicy(env, repoFullName, options);
}

/**
* Load only the repo-published focus manifest. This intentionally ignores maintainer/API-backed
* records so contributor-facing previews cannot infer private gate policy while still benefiting
* from fresh public repo-file cache entries.
*/
export async function loadPublicRepoFocusManifest(
env: Env,
repoFullName: string,
options: { fetcher?: RepoFocusManifestFetcher; maxAgeMs?: number; refresh?: boolean } = {},
): Promise<FocusManifest> {
return loadRepoFocusManifestWithCachePolicy(env, repoFullName, options, { publicOnly: true });
}

async function loadRepoFocusManifestWithCachePolicy(
env: Env,
repoFullName: string,
options: { fetcher?: RepoFocusManifestFetcher; maxAgeMs?: number; refresh?: boolean } = {},
cachePolicy: { publicOnly?: boolean } = {},
): Promise<FocusManifest> {
const fetcher = options.fetcher ?? fetchRepoFocusManifestFile;
const maxAgeMs = options.maxAgeMs ?? REPO_FOCUS_MANIFEST_MAX_AGE_MS;
if (!options.refresh) {
const cached = await readCachedManifest(env, repoFullName, maxAgeMs);
const cached = await readCachedManifest(env, repoFullName, maxAgeMs, cachePolicy);
if (cached) return cached;
}
let manifest: FocusManifest;
Expand Down Expand Up @@ -145,14 +167,15 @@ export async function upsertRepoFocusManifest(env: Env, repoFullName: string, ra
return manifest;
}

async function readCachedManifest(env: Env, repoFullName: string, maxAgeMs: number): Promise<FocusManifest | null> {
async function readCachedManifest(env: Env, repoFullName: string, maxAgeMs: number, options: { publicOnly?: boolean } = {}): Promise<FocusManifest | null> {
const [latest] = await listSignalSnapshots(env, REPO_FOCUS_MANIFEST_SIGNAL, repoFullName);
if (!latest) return null;
const manifest = parseFocusManifest(latest.payload);
const explicitSource =
latest.payload !== null && typeof latest.payload === "object" && !Array.isArray(latest.payload)
? (latest.payload as Record<string, JsonValue>).source
: undefined;
if (options.publicOnly && explicitSource !== "repo_file") return null;
if (explicitSource === "api_record") return manifest;
if (snapshotAgeMs(latest.generatedAt) > maxAgeMs) return null;
return manifest;
Expand Down
27 changes: 27 additions & 0 deletions test/unit/focus-manifest-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createTestEnv } from "../helpers/d1";
import type { JsonValue } from "../../src/types";
import {
fetchRepoFocusManifestFile,
loadPublicRepoFocusManifest,
loadRepoFocusManifest,
loadRepoFocusManifests,
upsertRepoFocusManifest,
Expand Down Expand Up @@ -87,6 +88,32 @@ describe("focus-manifest loader", () => {
expect(reloaded.source).toBe("api_record");
});

it("ignores API-backed records when loading a public-only repo manifest", async () => {
const env = createTestEnv();
await upsertRepoFocusManifest(env, "owner/public-only", { wantedPaths: ["private/"], gate: { linkedIssue: "block", readinessMinScore: 99 } });

const manifest = await loadPublicRepoFocusManifest(env, "owner/public-only", {
fetcher: async () => JSON.stringify({ wantedPaths: ["src/"], gate: { linkedIssue: "advisory" } }),
});

expect(manifest.source).toBe("repo_file");
expect(manifest.wantedPaths).toEqual(["src/"]);
expect(manifest.gate.linkedIssue).toBe("advisory");
expect(manifest.gate.readinessMinScore).toBeNull();
});

it("falls back to safe public defaults when only an API-backed record exists", async () => {
const env = createTestEnv();
await upsertRepoFocusManifest(env, "owner/no-public-file", { gate: { linkedIssue: "block", readinessMinScore: 99 } });

const manifest = await loadPublicRepoFocusManifest(env, "owner/no-public-file", { fetcher: async () => null });

expect(manifest.present).toBe(false);
expect(manifest.source).toBe("none");
expect(manifest.gate.linkedIssue).toBeNull();
expect(manifest.gate.readinessMinScore).toBeNull();
});

it("bulk-loads manifests for many repos with a concurrency cap", async () => {
const env = createTestEnv();
let active = 0;
Expand Down
Loading