From 31649f94b1e883e4512cf39b883d1c6ded3e7ac1 Mon Sep 17 00:00:00 2001 From: bitloi Date: Wed, 3 Jun 2026 00:29:29 +0200 Subject: [PATCH 1/2] feat(github-app): add maintainer trust checklist --- apps/gittensory-ui/public/openapi.json | 175 +++++++++++++++ .../site/app-panels/maintainer-panel.tsx | 179 +++++++++++++++ src/openapi/schemas.ts | 30 +++ src/signals/settings-preview.ts | 211 ++++++++++++++++++ test/integration/api.test.ts | 21 +- test/unit/settings-preview.test.ts | 63 ++++++ 6 files changed, 678 insertions(+), 1 deletion(-) diff --git a/apps/gittensory-ui/public/openapi.json b/apps/gittensory-ui/public/openapi.json index 7412f68379..27f965f67f 100644 --- a/apps/gittensory-ui/public/openapi.json +++ b/apps/gittensory-ui/public/openapi.json @@ -8108,6 +8108,180 @@ "detailLevel" ] }, + "installPreview": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "ready", + "needs_attention", + "blocked" + ] + }, + "summary": { + "type": "string" + }, + "readScope": { + "type": "array", + "items": { + "type": "string" + } + }, + "computedContext": { + "type": "array", + "items": { + "type": "string" + } + }, + "previewBehavior": { + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "ready", + "needs_attention", + "blocked" + ] + }, + "required": { + "type": "array", + "items": { + "type": "string" + } + }, + "missing": { + "type": "array", + "items": { + "type": "string" + } + }, + "missingEvents": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + } + }, + "required": [ + "status", + "required", + "missing", + "missingEvents", + "summary" + ] + }, + "publicOutputs": { + "type": "array", + "items": { + "type": "string" + } + }, + "privateOnlyContext": { + "type": "array", + "items": { + "type": "string" + } + }, + "commandAuthorization": { + "type": "array", + "items": { + "type": "string" + } + }, + "auditBehavior": { + "type": "array", + "items": { + "type": "string" + } + }, + "sanitizerBoundaries": { + "type": "array", + "items": { + "type": "string" + } + }, + "manualControls": { + "type": "array", + "items": { + "type": "string" + } + }, + "checklist": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "permissions", + "public_outputs", + "private_context", + "command_authorization", + "audit", + "sanitizer", + "manual_control" + ] + }, + "status": { + "type": "string", + "enum": [ + "ready", + "needs_attention", + "blocked" + ] + }, + "label": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "action": { + "type": "string" + } + }, + "required": [ + "id", + "category", + "status", + "label", + "summary", + "action" + ] + } + } + }, + "required": [ + "status", + "summary", + "readScope", + "computedContext", + "previewBehavior", + "permissions", + "publicOutputs", + "privateOnlyContext", + "commandAuthorization", + "auditBehavior", + "sanitizerBoundaries", + "manualControls", + "checklist" + ] + }, "warnings": { "type": "array", "items": { @@ -8128,6 +8302,7 @@ "previewComment", "appliedLabel", "checkRun", + "installPreview", "warnings", "summary" ] diff --git a/apps/gittensory-ui/src/components/site/app-panels/maintainer-panel.tsx b/apps/gittensory-ui/src/components/site/app-panels/maintainer-panel.tsx index f40d6b9ea5..9883788c9d 100644 --- a/apps/gittensory-ui/src/components/site/app-panels/maintainer-panel.tsx +++ b/apps/gittensory-ui/src/components/site/app-panels/maintainer-panel.tsx @@ -4,6 +4,7 @@ import { Bot, CheckCircle2, CircleSlash, + ListChecks, Play, RefreshCw, ShieldCheck, @@ -61,6 +62,44 @@ type MaintainerDashboard = { settingsPreview: { removed: string[]; added: string[] }; }; +type TrustChecklistStatus = "ready" | "needs_attention" | "blocked"; + +type InstallPreview = { + status: TrustChecklistStatus; + summary: string; + readScope: string[]; + computedContext: string[]; + previewBehavior: string[]; + permissions: { + status: TrustChecklistStatus; + required: string[]; + missing: string[]; + missingEvents: string[]; + summary: string; + }; + publicOutputs: string[]; + privateOnlyContext: string[]; + commandAuthorization: string[]; + auditBehavior: string[]; + sanitizerBoundaries: string[]; + manualControls: string[]; + checklist: Array<{ + id: string; + category: + | "permissions" + | "public_outputs" + | "private_context" + | "command_authorization" + | "audit" + | "sanitizer" + | "manual_control"; + status: TrustChecklistStatus; + label: string; + summary: string; + action: string; + }>; +}; + type SettingsPreviewResponse = { repoFullName: string; generatedAt: string; @@ -98,6 +137,7 @@ type SettingsPreviewResponse = { previewComment: string | null; appliedLabel: string | null; checkRun: { willCreate: boolean; title: string; detailLevel: string } | null; + installPreview: InstallPreview; warnings: string[]; summary: string; }; @@ -522,6 +562,8 @@ function PreviewResult({ + + {preview.warnings.length > 0 && (
@@ -574,6 +616,143 @@ function PreviewResult({ ); } +const TRUST_STATUS_PILL: Record = { + ready: "ready", + needs_attention: "warn", + blocked: "blocked", +}; + +const TRUST_STATUS_LABEL: Record = { + ready: "ready", + needs_attention: "attention", + blocked: "blocked", +}; + +function TrustChecklist({ installPreview }: { installPreview: InstallPreview }) { + const attentionItems = installPreview.checklist.filter((item) => item.status !== "ready"); + const detailGroups = [ + { + title: "Scope", + items: [ + ...installPreview.readScope, + ...installPreview.computedContext, + ...installPreview.previewBehavior, + ], + }, + { + title: "Public boundary", + items: [ + ...installPreview.publicOutputs, + ...installPreview.privateOnlyContext, + ...installPreview.sanitizerBoundaries, + ], + }, + { + title: "Controls", + items: [ + ...installPreview.commandAuthorization, + ...installPreview.auditBehavior, + ...installPreview.manualControls, + ], + }, + ]; + + return ( +
+
+
+
+ + Maintainer trust checklist +
+

{installPreview.summary}

+
+ + {TRUST_STATUS_LABEL[installPreview.status]} + +
+ +
+ {installPreview.checklist.map((item) => ( +
+
+ + {item.label} +
+
+

{item.summary}

+
+ + {TRUST_STATUS_LABEL[item.status]} + +
+ ))} +
+ + {attentionItems.length > 0 ? ( +
+
Review before enabling
+
    + {attentionItems.map((item) => ( +
  • + {item.label}:{" "} + {item.action} +
  • + ))} +
+
+ ) : null} + +
+ + Preview scope and controls + details + +
+ {detailGroups.map((group) => ( + + ))} +
+
+
+ ); +} + +function TrustDetailList({ title, items }: { title: string; items: string[] }) { + return ( +
+
+ {title} +
+
    + {items.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+ ); +} + +function TrustStatusIcon({ status }: { status: TrustChecklistStatus }) { + if (status === "blocked") return ; + if (status === "needs_attention") + return ; + return ; +} + function ActionState({ active, label }: { active: boolean; label: string }) { return (
; }; +export type RepoInstallPreviewStatus = "ready" | "needs_attention" | "blocked"; + +export type RepoInstallPreviewChecklistItem = { + id: string; + category: "permissions" | "public_outputs" | "private_context" | "command_authorization" | "audit" | "sanitizer" | "manual_control"; + status: RepoInstallPreviewStatus; + label: string; + summary: string; + action: string; +}; + +export type RepoInstallPreview = { + status: RepoInstallPreviewStatus; + summary: string; + readScope: string[]; + computedContext: string[]; + previewBehavior: string[]; + permissions: { + status: RepoInstallPreviewStatus; + required: string[]; + missing: string[]; + missingEvents: string[]; + summary: string; + }; + publicOutputs: string[]; + privateOnlyContext: string[]; + commandAuthorization: string[]; + auditBehavior: string[]; + sanitizerBoundaries: string[]; + manualControls: string[]; + checklist: RepoInstallPreviewChecklistItem[]; +}; + export type RepoSettingsPreview = { repoFullName: string; generatedAt: string; @@ -151,6 +184,7 @@ export type RepoSettingsPreview = { previewComment: string | null; appliedLabel: string | null; checkRun: { willCreate: boolean; title: string; detailLevel: RepositorySettings["checkRunDetailLevel"] } | null; + installPreview: RepoInstallPreview; warnings: string[]; summary: string; }; @@ -192,6 +226,13 @@ export function buildRepoSettingsPreview(args: { : null; const warnings = buildWarnings(settings, decision, args.installation); + const installPreview = buildRepoInstallPreview({ + repo, + settings, + installation: args.installation, + decision, + appliedLabel: decision.willLabel ? settings.gittensorLabel : null, + }); return { repoFullName, @@ -214,6 +255,7 @@ export function buildRepoSettingsPreview(args: { previewComment, appliedLabel: decision.willLabel ? settings.gittensorLabel : null, checkRun: decision.willCheckRun ? { willCreate: true, title: "Gittensory context posted", detailLevel: settings.checkRunDetailLevel } : null, + installPreview, warnings, summary: decision.skipped ? `Sample PR would be skipped: ${decision.summary}` @@ -243,6 +285,175 @@ function buildWarnings(settings: RepositorySettings, decision: PublicSurfaceDeci return warnings; } +function buildRepoInstallPreview(args: { + repo: RepositoryRecord | null; + settings: RepositorySettings; + installation: InstallationHealthSummary | null; + decision: PublicSurfaceDecision; + appliedLabel: string | null; +}): RepoInstallPreview { + const required = requiredInstallPermissions(args.settings, args.decision); + const missing = activeMissingPermissions(args.settings, args.decision, args.installation); + const missingEvents = args.installation?.missingEvents ?? []; + const permissionStatus: RepoInstallPreviewStatus = !args.installation || args.installation.status === "broken" ? "blocked" : missing.length > 0 || missingEvents.length > 0 || args.installation.status === "needs_attention" ? "needs_attention" : "ready"; + const publicOutputStatus: RepoInstallPreviewStatus = args.settings.commentMode === "all_prs" ? "needs_attention" : "ready"; + const commandAuthorizationStatus: RepoInstallPreviewStatus = !args.installation ? "blocked" : new Set(args.installation.missingPermissions).has("issues") ? "needs_attention" : "ready"; + const manualControlStatus: RepoInstallPreviewStatus = args.settings.commentMode === "all_prs" ? "needs_attention" : "ready"; + const checklist: RepoInstallPreviewChecklistItem[] = [ + { + id: "permissions", + category: "permissions", + status: permissionStatus, + label: "Permissions and events", + summary: permissionSummary(args.installation, missing, missingEvents), + action: permissionStatus === "ready" ? "No permission change is needed for this previewed behavior." : "Refresh installation health, then approve the missing permission or webhook event before enabling public output.", + }, + { + id: "public-outputs", + category: "public_outputs", + status: publicOutputStatus, + label: "Public outputs", + summary: publicOutputSummary(args.decision), + action: publicOutputStatus === "ready" ? "Review the rendered public preview before enabling this repo." : "Review all-PR mode carefully; confirmed-miner-only output is quieter for first enablement.", + }, + { + id: "private-context", + category: "private_context", + status: "ready", + label: "Private-only context", + summary: "Decision packs, blocker detail, maintainer packet evidence, and scoring evidence stay on authenticated API or MCP surfaces.", + action: "Keep private evidence out of public issue bodies, PR bodies, comments, and copied snippets.", + }, + { + id: "command-authorization", + category: "command_authorization", + status: commandAuthorizationStatus, + label: "Command authorization", + summary: "Public command responses require a maintainer or confirmed PR author; maintainer queue commands require owner, member, or collaborator context.", + action: commandAuthorizationStatus === "ready" ? "Use command previews to confirm actor and permission behavior before relying on repo commands." : "Restore Issues: write before enabling public command responses.", + }, + { + id: "audit-behavior", + category: "audit", + status: "ready", + label: "Audit behavior", + summary: "This preview is read-only; live webhook skips, command handling, auth, and usage paths are recorded through audit or product-usage logs.", + action: "Use preview output for review; use live audit records for production behavior after enablement.", + }, + { + id: "sanitizer-boundaries", + category: "sanitizer", + status: "ready", + label: "Sanitizer boundaries", + summary: "Public comments and copied snippets are sanitized before they leave the Worker.", + action: "Private evidence remains authenticated-only and should not be copied into public surfaces.", + }, + { + id: "manual-controls", + category: "manual_control", + status: manualControlStatus, + label: "Manual controls", + summary: "Public surface mode, comments, labels, check runs, maintainer-author inclusion, and linked-issue requirements remain repo-controlled settings.", + action: manualControlStatus === "ready" ? "Enable only the specific public surface you want after previewing it." : "Switch away from all-PR mode unless broad public output is intentional.", + }, + ]; + const status = checklist.some((item) => item.status === "blocked") ? "blocked" : checklist.some((item) => item.status === "needs_attention") ? "needs_attention" : "ready"; + + return { + status, + summary: + status === "ready" + ? "Install preview is ready for maintainer review before enabling repo commands." + : status === "blocked" + ? "Install preview has a blocking setup gap before repo commands should be enabled." + : "Install preview is usable, but one or more setup details need maintainer attention.", + readScope: [ + "Cached repository metadata, issues, pull requests, labels, linked issues, repo settings, and installation health.", + args.repo?.isInstalled ? "GitHub App installation metadata for the selected repository." : "No GitHub App installation metadata is linked to this repository yet.", + ], + computedContext: [ + "Public surface decision for comment, label, check-run, or skip behavior.", + "Queue, collision, contributor profile, and preflight context for the sample public preview.", + ], + previewBehavior: [ + "The dry-run preview does not create GitHub comments, labels, check runs, or installation changes.", + "A public comment body is rendered only when current settings would comment for the sample PR.", + ], + permissions: { + status: permissionStatus, + required, + missing, + missingEvents, + summary: permissionSummary(args.installation, missing, missingEvents), + }, + publicOutputs: publicOutputsFor(args.decision, args.appliedLabel), + privateOnlyContext: [ + "Decision packs, blocker details, maintainer packet evidence, and scoring evidence stay authenticated-only.", + "This preview uses cached metadata and the supplied sample PR fields; it does not upload repository source.", + ], + commandAuthorization: [ + "Maintainer-only commands require owner, member, or collaborator context.", + "Contributor-invoked public commands require the commenter to be the confirmed PR author.", + "Private API commands require authenticated control-panel access and do not post public GitHub output.", + ], + auditBehavior: [ + "This preview is read-only and does not mutate GitHub.", + "Live webhook skips, command handling, miner-detection fallbacks, auth, and usage paths are audit or product-usage logged.", + ], + sanitizerBoundaries: [ + "Public GitHub comments and copied snippets are sanitized before posting.", + "Credential/key material, compensation estimates, trust metrics, score-prediction claims, private review evidence, private scoring evidence, and gaming language stay out of public output.", + "Private evidence is not copied into public comments, issue bodies, PR bodies, or extension public panels.", + ], + manualControls: [ + "Public surface mode, comment mode, label name, check-run mode, maintainer-author inclusion, and linked-issue requirements remain repo settings.", + "Maintainers preview first, then enable the specific public output they want.", + ], + checklist, + }; +} + +function requiredInstallPermissions(settings: RepositorySettings, decision: PublicSurfaceDecision): string[] { + const permissions = new Set(["metadata: read", "pull_requests: read"]); + if (decision.willComment || decision.willLabel || shouldPublishPrComment(settings) || shouldApplyPrLabel(settings)) permissions.add("issues: write"); + if (decision.willCheckRun || settings.checkRunMode === "enabled") permissions.add("checks: write"); + return [...permissions]; +} + +function activeMissingPermissions(settings: RepositorySettings, decision: PublicSurfaceDecision, installation: InstallationHealthSummary | null): string[] { + if (!installation) return []; + const missing = new Set(installation.missingPermissions); + const active: string[] = []; + if ((decision.willComment || decision.willLabel || shouldPublishPrComment(settings) || shouldApplyPrLabel(settings)) && missing.has("issues")) active.push("issues"); + if ((decision.willCheckRun || settings.checkRunMode === "enabled") && missing.has("checks")) active.push("checks"); + return active; +} + +function permissionSummary(installation: InstallationHealthSummary | null, missing: string[], missingEvents: string[]): string { + if (!installation) return "No installation health is cached for this repository."; + if (installation.status === "broken") return "Installation health is broken and needs recovery before enablement."; + if (missing.length > 0 || missingEvents.length > 0) { + return `Installation needs attention: ${[missing.length > 0 ? `missing permission(s) ${missing.join(", ")}` : "", missingEvents.length > 0 ? `missing webhook event(s) ${missingEvents.join(", ")}` : ""].filter(Boolean).join("; ")}.`; + } + if (installation.status === "needs_attention") return "Installation health needs attention; review remediation before enabling public output."; + return "Required permissions and webhook events are ready for the previewed behavior."; +} + +function publicOutputsFor(decision: PublicSurfaceDecision, appliedLabel: string | null): string[] { + if (decision.skipped) return [`No public output for this sample: ${decision.summary}`]; + const outputs = [ + ...(decision.willComment ? ["One sanitized sticky PR comment."] : []), + ...(decision.willLabel ? [`Configured label "${appliedLabel ?? "gittensor"}".`] : []), + ...(decision.willCheckRun ? ["Minimal GitHub check run."] : []), + ]; + return outputs.length > 0 ? outputs : ["No public comment, label, or check run for this sample."]; +} + +function publicOutputSummary(decision: PublicSurfaceDecision): string { + if (decision.skipped) return `Current sample is skipped: ${decision.summary}`; + return decision.actions.includes("none") ? "The sample qualifies, but no public output action is enabled." : `Current sample would create: ${decision.actions.join(", ")}.`; +} + function buildSamplePreviewComment(args: { repoFullName: string; repo: RepositoryRecord | null; diff --git a/test/integration/api.test.ts b/test/integration/api.test.ts index 89f9122074..d4c617134a 100644 --- a/test/integration/api.test.ts +++ b/test/integration/api.test.ts @@ -474,11 +474,29 @@ describe("api routes", () => { env, ); expect(minerPreview.status).toBe(200); - const minerPreviewBody = (await minerPreview.json()) as { decision: { willComment: boolean; skipped: boolean }; previewComment: string | null; settings: { publicSurface: string } }; + const minerPreviewBody = (await minerPreview.json()) as { + decision: { willComment: boolean; skipped: boolean }; + previewComment: string | null; + settings: { publicSurface: string }; + installPreview: { + status: string; + permissions: { required: string[]; status: string }; + checklist: Array<{ id: string; status: string; summary: string; action: string }>; + }; + }; expect(minerPreviewBody.decision.skipped).toBe(false); expect(minerPreviewBody.decision.willComment).toBe(true); expect(minerPreviewBody.previewComment).toContain("Gittensory contribution context"); expect(minerPreviewBody.previewComment).not.toMatch(/wallet|hotkey|trust score|scoreability|payout/i); + expect(minerPreviewBody.installPreview).toMatchObject({ + status: "ready", + permissions: { required: expect.arrayContaining(["issues: write"]) }, + checklist: expect.arrayContaining([ + expect.objectContaining({ id: "permissions", status: "ready" }), + expect.objectContaining({ id: "sanitizer-boundaries", status: "ready" }), + expect.objectContaining({ id: "manual-controls", status: "ready" }), + ]), + }); const invalidPreview = await app.request( "/v1/repos/entrius/allways-ui/settings-preview", @@ -491,6 +509,7 @@ describe("api routes", () => { expect(unknownRepoPreview.status).toBe(200); await expect(unknownRepoPreview.json()).resolves.toMatchObject({ installation: null, + installPreview: { status: "blocked", permissions: { status: "blocked" } }, sample: { authorLogin: "sample-contributor", minerStatus: "confirmed" }, }); diff --git a/test/unit/settings-preview.test.ts b/test/unit/settings-preview.test.ts index e39899906d..c61332be3d 100644 --- a/test/unit/settings-preview.test.ts +++ b/test/unit/settings-preview.test.ts @@ -2,6 +2,9 @@ import { describe, expect, it } from "vitest"; import { buildRepoSettingsPreview, decidePublicSurface, type InstallationHealthSummary } from "../../src/signals/settings-preview"; import type { IssueRecord, PullRequestRecord, RepositoryRecord, RepositorySettings } from "../../src/types"; +const FORBIDDEN_INSTALL_PREVIEW_PUBLIC_LANGUAGE = + /\b(wallet|hotkey|coldkey|raw[-\s]?trust|trust[-\s]?score|reward[-\s]?estimate|payout|farming(?:[-\s]?language)?|private[-\s]?reviewability|private[-\s]?scoreability|scoreability|public[-\s]?score[-\s]?(?:estimate|prediction)|estimated[-\s]?score|score[-\s]?estimate)\b/i; + const repo: RepositoryRecord = { fullName: "entrius/allways-ui", owner: "entrius", @@ -101,6 +104,34 @@ describe("buildRepoSettingsPreview", () => { expect(preview.appliedLabel).toBe("gittensor"); expect(preview.previewComment).toContain("Gittensory contribution context"); expect(preview.warnings).toHaveLength(0); + expect(preview.installPreview).toMatchObject({ + status: "ready", + permissions: { status: "ready", required: expect.arrayContaining(["metadata: read", "pull_requests: read", "issues: write"]) }, + publicOutputs: expect.arrayContaining(["One sanitized sticky PR comment.", 'Configured label "gittensor".']), + checklist: expect.arrayContaining([ + expect.objectContaining({ id: "permissions", status: "ready" }), + expect.objectContaining({ id: "public-outputs", status: "ready" }), + expect.objectContaining({ id: "private-context", status: "ready" }), + expect.objectContaining({ id: "command-authorization", status: "ready" }), + expect.objectContaining({ id: "audit-behavior", status: "ready" }), + expect.objectContaining({ id: "sanitizer-boundaries", status: "ready" }), + expect.objectContaining({ id: "manual-controls", status: "ready" }), + ]), + }); + expect(preview.installPreview.readScope.join(" ")).toMatch(/repository metadata/i); + expect(preview.installPreview.computedContext.join(" ")).toMatch(/Public surface decision/i); + expect(preview.installPreview.privateOnlyContext.join(" ")).toMatch(/authenticated-only/i); + expect(preview.installPreview.commandAuthorization.join(" ")).toMatch(/Maintainer-only commands/i); + expect(preview.installPreview.auditBehavior.join(" ")).toMatch(/read-only/i); + expect(preview.installPreview.manualControls.join(" ")).toMatch(/repo settings/i); + expect( + JSON.stringify([ + preview.installPreview.publicOutputs, + preview.installPreview.sanitizerBoundaries, + preview.installPreview.manualControls, + preview.installPreview.checklist.map((item) => [item.summary, item.action]), + ]), + ).not.toMatch(FORBIDDEN_INSTALL_PREVIEW_PUBLIC_LANGUAGE); }); it("uses safe defaults for an empty sample preview", () => { @@ -117,6 +148,12 @@ describe("buildRepoSettingsPreview", () => { sample: { authorLogin: "miner", minerStatus: "confirmed" }, }); expect(preview.warnings.some((warning) => /Issues: write/.test(warning))).toBe(true); + expect(preview.installPreview.status).toBe("needs_attention"); + expect(preview.installPreview.permissions).toMatchObject({ status: "needs_attention", missing: ["issues"] }); + expect(preview.installPreview.checklist.find((item) => item.id === "permissions")).toMatchObject({ + status: "needs_attention", + action: expect.stringContaining("approve the missing permission"), + }); }); it("explains a missing optional Checks: write permission only when check runs are enabled", () => { @@ -168,6 +205,11 @@ describe("buildRepoSettingsPreview", () => { it("warns when installation health is unknown", () => { const preview = buildRepoSettingsPreview({ ...base, settings: settings(), installation: null, sample: { authorLogin: "miner", minerStatus: "confirmed" } }); expect(preview.warnings.some((warning) => /Installation health is unknown/.test(warning))).toBe(true); + expect(preview.installPreview).toMatchObject({ + status: "blocked", + permissions: { status: "blocked", required: expect.arrayContaining(["issues: write"]), missing: [], missingEvents: [] }, + }); + expect(preview.installPreview.checklist.find((item) => item.id === "permissions")).toMatchObject({ status: "blocked" }); }); it("explains missing webhook event subscriptions", () => { @@ -178,6 +220,7 @@ describe("buildRepoSettingsPreview", () => { sample: { authorLogin: "miner", minerStatus: "confirmed" }, }); expect(preview.warnings).toEqual(expect.arrayContaining([expect.stringMatching(/pull_request webhook event/)])); + expect(preview.installPreview.permissions).toMatchObject({ status: "needs_attention", missingEvents: ["pull_request"] }); }); it("falls back to the installation status warning when no specific remediation is available", () => { @@ -188,6 +231,26 @@ describe("buildRepoSettingsPreview", () => { sample: { authorLogin: "miner", minerStatus: "confirmed" }, }); expect(preview.warnings).toEqual(["Installation status is broken; review the installation health endpoint for remediation steps."]); + expect(preview.installPreview.status).toBe("blocked"); + expect(preview.installPreview.permissions.summary).toMatch(/broken/i); + }); + + it("marks broad all-PR output as needing maintainer attention before enablement", () => { + const preview = buildRepoSettingsPreview({ + ...base, + settings: settings({ commentMode: "all_prs" }), + installation: healthyInstall, + sample: { authorLogin: "miner", minerStatus: "confirmed" }, + }); + expect(preview.installPreview.status).toBe("needs_attention"); + expect(preview.installPreview.checklist.find((item) => item.id === "public-outputs")).toMatchObject({ + status: "needs_attention", + action: expect.stringContaining("confirmed-miner-only"), + }); + expect(preview.installPreview.checklist.find((item) => item.id === "manual-controls")).toMatchObject({ + status: "needs_attention", + action: expect.stringContaining("all-PR mode"), + }); }); it("never leaks private scoring/trust terms into the preview comment (sanitizer regression)", () => { From 48b75d8b8ecfcc93a1cdfd8550c9ed553eee05c0 Mon Sep 17 00:00:00 2001 From: bitloi Date: Wed, 3 Jun 2026 12:25:05 +0200 Subject: [PATCH 2/2] test: cover policy readiness branches --- test/unit/repo-policy-readiness.test.ts | 82 +++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/unit/repo-policy-readiness.test.ts b/test/unit/repo-policy-readiness.test.ts index 83c036f304..8bbddf0fc7 100644 --- a/test/unit/repo-policy-readiness.test.ts +++ b/test/unit/repo-policy-readiness.test.ts @@ -134,6 +134,14 @@ function input(overrides: Partial = {}): RepoPolicyRea } describe("buildRepoPolicyReadiness", () => { + it("summarizes a clean policy when no warnings are present", () => { + const report = buildRepoPolicyReadiness(input()); + + expect(report.publicWarnings).toEqual([]); + expect(report.droppedPublicWarnings).toEqual([]); + expect(report.summary).toBe("Policy readiness has no public-safe warnings for owner review."); + }); + it("warns when direct-PR policy is loose", () => { const report = buildRepoPolicyReadiness( input({ @@ -158,6 +166,32 @@ describe("buildRepoPolicyReadiness", () => { expect(report.publicWarnings.flatMap((warning) => [warning.title, warning.detail, warning.action]).every(isFocusManifestPublicSafe)).toBe(true); }); + it("warns when contribution scope only defines blocked work", () => { + const report = buildRepoPolicyReadiness( + input({ + settings: settings({ requireLinkedIssue: true }), + focusManifest: parseFocusManifest({ + blockedPaths: ["docs/"], + linkedIssuePolicy: "optional", + testExpectations: ["Run npm run test:ci."], + }), + }), + ); + + expect(report.ownerContext).toMatchObject({ + wantedPathCount: 0, + blockedPathCount: 1, + issuePolicy: "direct_pr_requires_linked_issue", + }); + expect(report.publicWarnings.map((warning) => warning.code)).toEqual( + expect.arrayContaining([ + "contribution_scope_unclear", + "blocked_work_without_wanted_scope", + "linked_issue_policy_mismatch", + ]), + ); + }); + it("warns about issue-discovery policy conflicts and intake gaps", () => { const report = buildRepoPolicyReadiness( input({ @@ -181,6 +215,31 @@ describe("buildRepoPolicyReadiness", () => { ); }); + it("marks blocked issue-discovery intake as critical", () => { + const report = buildRepoPolicyReadiness( + input({ + lane: lane({ lane: "issue_discovery", issueDiscoveryShare: 1, directPrShare: 0 }), + focusManifest: parseFocusManifest({ + wantedPaths: ["src/"], + linkedIssuePolicy: "required", + issueDiscoveryPolicy: "neutral", + testExpectations: ["Run npm run test:ci."], + }), + contributorIntakeHealth: intake({ level: "blocked" }), + }), + ); + + expect(report.ownerContext.issuePolicy).toBe("issue_discovery_enabled"); + expect(report.publicWarnings).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + code: "issue_discovery_intake_not_ready", + severity: "critical", + }), + ]), + ); + }); + it("warns when a direct-PR repo has issue-discovery encouraged in focus policy", () => { const report = buildRepoPolicyReadiness( input({ @@ -324,4 +383,27 @@ describe("buildRepoPolicyReadiness", () => { ]), ); }); + + it("uses empty owner-context defaults when the focus manifest is omitted", () => { + const report = buildRepoPolicyReadiness(input({ focusManifest: undefined })); + + expect(report).toMatchObject({ + present: false, + ownerContext: { + manifestPresent: false, + manifestSource: "none", + privateNoteCount: 0, + manifestWarningCount: 0, + wantedPathCount: 0, + blockedPathCount: 0, + validationExpectationCount: 0, + issueDiscoveryPolicy: "neutral", + }, + }); + expect(report.publicWarnings).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: "focus_policy_missing" }), + ]), + ); + }); });