diff --git a/src/github/backfill.ts b/src/github/backfill.ts index 5b48885508..90cff9820a 100644 --- a/src/github/backfill.ts +++ b/src/github/backfill.ts @@ -731,9 +731,9 @@ export function enrichInstallationHealth(health: InstallationHealthRecord) { const requiredPermissions = { ...REQUIRED_INSTALLATION_PERMISSIONS, ...(missingPermissions.has("checks") ? OPTIONAL_CHECK_RUN_PERMISSION : {}), - // pull_requests is missing ONLY when the refresh required write (an acting autonomy) and it was not granted, so - // surface the write requirement in the remediation rather than the baseline read. (#audit-install-health) - ...(missingPermissions.has("pull_requests") ? OPTIONAL_PR_WRITE_PERMISSION : {}), + // Persisted health stores only the missing permission name. If pull_requests is already granted at read level, + // a missing pull_requests entry can only mean an acting autonomy needs write; otherwise preserve baseline read. + ...(missingPermissions.has("pull_requests") && permissionSatisfies(health.permissions.pull_requests, "read") ? OPTIONAL_PR_WRITE_PERMISSION : {}), }; return { ...health, diff --git a/test/unit/backfill.test.ts b/test/unit/backfill.test.ts index 4a781af46d..523e793ae0 100644 --- a/test/unit/backfill.test.ts +++ b/test/unit/backfill.test.ts @@ -457,6 +457,27 @@ describe("GitHub backfill", () => { ); }); + it("keeps baseline pull_requests:read remediation when the permission is absent (#audit-install-health least privilege)", () => { + const health = enrichInstallationHealth({ + installationId: 127, + accountLogin: "JSONbored", + repositorySelection: "selected", + installedReposCount: 1, + registeredInstalledCount: 1, + status: "needs_attention", + missingPermissions: ["pull_requests"], + missingEvents: [], + permissions: { metadata: "read", issues: "write" }, + events: ["issues", "issue_comment", "pull_request", "repository"], + checkedAt: "2026-06-05T00:00:00.000Z", + }); + + expect(health.requiredPermissions).toMatchObject({ pull_requests: "read" }); + expect(health.permissionRemediation).toEqual( + expect.arrayContaining([expect.objectContaining({ permission: "pull_requests", requiredAccess: "read", ok: false, action: "Set repository permission pull_requests to read." })]), + ); + }); + it("requires Checks write only for repos with check runs enabled", async () => { const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() }); await seedRegisteredRepo(env);