From da057ecba24ecfe0e1688b63939721735fb37536 Mon Sep 17 00:00:00 2001 From: web-dev0521 Date: Wed, 3 Jun 2026 01:02:51 -0600 Subject: [PATCH] feat(settings): define focus-manifest policy schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds FocusManifestPolicy and compileFocusManifestPolicy() to produce a normalized, machine-readable schema from a repo focus manifest before wiring downstream owner or maintainer workflows. - src/signals/focus-manifest.ts: - New FocusManifestLanePreference type ("preferred"|"neutral"|"discouraged") - New sub-types: FocusManifestPolicyContributionLanes, FocusManifestPolicyDiscouragedWork, FocusManifestPolicyLabelExpectations, FocusManifestPolicyValidationExpectations - New FocusManifestPolicy type with explicit publicSafe / authenticated field split — publicSafe is contributor-facing; authenticated is for repo owner/maintainer surfaces only - New compileFocusManifestPolicy(manifest) function — deterministic, same manifest always produces the same policy - Private helpers: policyDirectPrLane, policyIssueDiscoveryLane, policyEntryGuidance, policyReadinessWarnings, policyPublicSummary - test/unit/focus-manifest.test.ts (14 new tests): - Minimal: absent manifest → neutral defaults, unconstrained summary - Malformed: parse warnings forwarded to authenticated.parseWarnings - Typical: fully specified manifest covers all schema fields end-to-end - Missing-field: linkedIssuePolicy only, issueDiscoveryPolicy only, blockedPaths only — each branch verified independently - Readiness warnings: discouraged-ID-no-paths, required-link-no-guidance - Public/private separation: maintainerContext absent from publicSafe, forbidden publicNotes/testExpectations filtered before publicSafe - Source preservation across repo_file / api_record / none - Property-based sanitizer (400 iterations, seeded LCG): all publicSafe text fields pass isFocusManifestPublicSafe across random inputs --- src/signals/focus-manifest.ts | 183 ++++++++++++++++++++++++++++ test/unit/focus-manifest.test.ts | 200 +++++++++++++++++++++++++++++++ 2 files changed, 383 insertions(+) diff --git a/src/signals/focus-manifest.ts b/src/signals/focus-manifest.ts index b3b02d1708..60ad0a6d5b 100644 --- a/src/signals/focus-manifest.ts +++ b/src/signals/focus-manifest.ts @@ -360,3 +360,186 @@ function summarize(manifest: FocusManifest, blocked: string[], wanted: string[]) if (manifest.wantedPaths.length > 0) return "Maintainer focus manifest: change is outside the wanted areas."; return "Maintainer focus manifest applied with no path-specific verdict."; } + +// ─── Focus Manifest Policy Schema ──────────────────────────────────────────── + +/** Preference signal for a contribution lane derived from the focus manifest. */ +export type FocusManifestLanePreference = "preferred" | "neutral" | "discouraged"; + +/** + * Public-safe contribution lane preferences derived from the manifest. + * Safe to surface on contributor-facing outputs. + */ +export type FocusManifestPolicyContributionLanes = { + directPrLane: FocusManifestLanePreference; + issueDiscoveryLane: FocusManifestLanePreference; + preferredEntryPaths: string[]; +}; + +/** + * Public-safe discouragement signals: blocked paths and issue-discovery status. + * Safe to surface on contributor-facing outputs. + */ +export type FocusManifestPolicyDiscouragedWork = { + blockedEntryPaths: string[]; + issueDiscoveryDiscouraged: boolean; +}; + +/** + * Public-safe label and linked-issue expectations. + * Safe to surface on contributor-facing outputs. + */ +export type FocusManifestPolicyLabelExpectations = { + preferredLabels: string[]; + linkedIssuePolicy: FocusManifestLinkedIssuePolicy; +}; + +/** + * Public-safe validation expectations derived from the manifest's test and issue-link policies. + * Safe to surface on contributor-facing outputs. + */ +export type FocusManifestPolicyValidationExpectations = { + testExpectations: string[]; + linkedIssueRequired: boolean; + linkedIssuePreferred: boolean; +}; + +/** + * Normalized policy schema compiled from a repo focus manifest. + * + * `publicSafe` fields contain only contributor-safe guidance — they are free of + * maintainer-private notes, scoreability, reviewability, reward/risk, wallet, + * hotkey, and raw trust context. + * + * `authenticated` fields are intended for repo owner and maintainer surfaces only + * and must never be forwarded to contributor-facing GitHub output. + */ +export type FocusManifestPolicy = { + present: boolean; + source: FocusManifestSource; + publicSafe: { + contributionLanes: FocusManifestPolicyContributionLanes; + discouragedWork: FocusManifestPolicyDiscouragedWork; + labelExpectations: FocusManifestPolicyLabelExpectations; + validationExpectations: FocusManifestPolicyValidationExpectations; + entryGuidance: string[]; + summary: string; + }; + authenticated: { + readinessWarnings: string[]; + parseWarnings: string[]; + maintainerContext: string[]; + }; +}; + +/** + * Compile a {@link FocusManifest} into a normalized, machine-readable + * {@link FocusManifestPolicy}. + * + * The result is deterministic: the same manifest always produces the same policy. + * Public-safe fields and private/authenticated fields are strictly separated so + * callers can route each subset to the appropriate surface without manual filtering. + */ +export function compileFocusManifestPolicy(manifest: FocusManifest): FocusManifestPolicy { + if (!manifest.present) { + const readinessWarnings = manifest.warnings.length > 0 + ? manifest.warnings + : ["No maintainer focus manifest found; contribution policy uses deterministic defaults."]; + return { + present: false, + source: manifest.source, + publicSafe: { + contributionLanes: { directPrLane: "neutral", issueDiscoveryLane: "neutral", preferredEntryPaths: [] }, + discouragedWork: { blockedEntryPaths: [], issueDiscoveryDiscouraged: false }, + labelExpectations: { preferredLabels: [], linkedIssuePolicy: "optional" }, + validationExpectations: { testExpectations: [], linkedIssueRequired: false, linkedIssuePreferred: false }, + entryGuidance: [], + summary: "No maintainer focus manifest; contribution policy is unconstrained.", + }, + authenticated: { + readinessWarnings, + parseWarnings: manifest.warnings, + maintainerContext: [], + }, + }; + } + + const directPrLane = policyDirectPrLane(manifest); + const issueDiscoveryLane = policyIssueDiscoveryLane(manifest); + const entryGuidance = policyEntryGuidance(manifest); + + return { + present: true, + source: manifest.source, + publicSafe: { + contributionLanes: { + directPrLane, + issueDiscoveryLane, + preferredEntryPaths: manifest.wantedPaths.filter(isFocusManifestPublicSafe), + }, + discouragedWork: { + blockedEntryPaths: manifest.blockedPaths.filter(isFocusManifestPublicSafe), + issueDiscoveryDiscouraged: manifest.issueDiscoveryPolicy === "discouraged", + }, + labelExpectations: { + preferredLabels: manifest.preferredLabels.filter(isFocusManifestPublicSafe), + linkedIssuePolicy: manifest.linkedIssuePolicy, + }, + validationExpectations: { + testExpectations: manifest.testExpectations.filter(isFocusManifestPublicSafe), + linkedIssueRequired: manifest.linkedIssuePolicy === "required", + linkedIssuePreferred: manifest.linkedIssuePolicy === "preferred", + }, + entryGuidance, + summary: policyPublicSummary(manifest, directPrLane, issueDiscoveryLane), + }, + authenticated: { + readinessWarnings: policyReadinessWarnings(manifest), + parseWarnings: manifest.warnings, + maintainerContext: manifest.maintainerNotes, + }, + }; +} + +function policyDirectPrLane(manifest: FocusManifest): FocusManifestLanePreference { + if (manifest.issueDiscoveryPolicy === "encouraged") return "discouraged"; + if (manifest.wantedPaths.length > 0) return "preferred"; + return "neutral"; +} + +function policyIssueDiscoveryLane(manifest: FocusManifest): FocusManifestLanePreference { + if (manifest.issueDiscoveryPolicy === "encouraged") return "preferred"; + if (manifest.issueDiscoveryPolicy === "discouraged") return "discouraged"; + return "neutral"; +} + +function policyEntryGuidance(manifest: FocusManifest): string[] { + const guidance: string[] = []; + if (manifest.wantedPaths.length > 0) guidance.push(`Focus changes on maintainer-wanted areas: ${manifest.wantedPaths.slice(0, 5).join(", ")}.`); + if (manifest.blockedPaths.length > 0) guidance.push(`Avoid maintainer-blocked areas: ${manifest.blockedPaths.slice(0, 5).join(", ")}.`); + if (manifest.preferredLabels.length > 0) guidance.push(`Apply a maintainer-preferred label: ${manifest.preferredLabels.slice(0, 3).join(", ")}.`); + if (manifest.linkedIssuePolicy === "required") guidance.push("Link a tracked issue before opening a PR."); + else if (manifest.linkedIssuePolicy === "preferred") guidance.push("Link a tracked issue if one exists."); + if (manifest.issueDiscoveryPolicy === "encouraged") guidance.push("Issue discovery reports are welcomed; search for gaps before opening a PR."); + else if (manifest.issueDiscoveryPolicy === "discouraged") guidance.push("Prefer direct fixes over new issue reports."); + for (const note of manifest.publicNotes) { + if (isFocusManifestPublicSafe(note)) guidance.push(note); + } + return [...new Set(guidance)].filter(isFocusManifestPublicSafe); +} + +function policyReadinessWarnings(manifest: FocusManifest): string[] { + const warnings: string[] = []; + if (manifest.blockedPaths.length > 0) warnings.push(`${manifest.blockedPaths.length} blocked area(s) declared; contributors should confirm scope before opening PRs.`); + if (manifest.issueDiscoveryPolicy === "discouraged" && manifest.wantedPaths.length === 0) warnings.push("Issue discovery is discouraged but no wanted paths are declared; contributors have limited guidance on preferred work areas."); + if (manifest.linkedIssuePolicy === "required" && manifest.wantedPaths.length === 0 && manifest.preferredLabels.length === 0) warnings.push("Linked issues are required but no preferred labels or wanted paths are configured; consider adding wanted paths or preferred labels to guide contributors."); + return warnings; +} + +function policyPublicSummary(manifest: FocusManifest, directPrLane: FocusManifestLanePreference, issueDiscoveryLane: FocusManifestLanePreference): string { + if (issueDiscoveryLane === "preferred" && directPrLane === "discouraged") return "Issue-discovery is the preferred contribution mode; direct PRs are discouraged."; + if (issueDiscoveryLane === "discouraged" && manifest.wantedPaths.length > 0) return "Direct PRs on the wanted areas are preferred; issue-discovery submissions are discouraged."; + if (directPrLane === "preferred") return "Direct PRs on the maintainer-wanted areas are preferred."; + if (issueDiscoveryLane === "discouraged") return "Direct PRs are preferred; issue-discovery submissions are discouraged."; + return "Contribution policy is guided by the maintainer focus manifest."; +} diff --git a/test/unit/focus-manifest.test.ts b/test/unit/focus-manifest.test.ts index 0d66dd9fbe..804c2e6b4e 100644 --- a/test/unit/focus-manifest.test.ts +++ b/test/unit/focus-manifest.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; import { buildFocusManifestGuidance, + compileFocusManifestPolicy, isFocusManifestPublicSafe, matchesManifestPath, parseFocusManifest, @@ -230,6 +231,205 @@ describe("buildFocusManifestGuidance", () => { }); }); +describe("compileFocusManifestPolicy", () => { + // ── Minimal: absent manifest ─────────────────────────────────────────── + it("returns unconstrained policy with neutral lanes for an absent manifest", () => { + const policy = compileFocusManifestPolicy(parseFocusManifest(null)); + expect(policy.present).toBe(false); + expect(policy.source).toBe("none"); + expect(policy.publicSafe.contributionLanes).toEqual({ directPrLane: "neutral", issueDiscoveryLane: "neutral", preferredEntryPaths: [] }); + expect(policy.publicSafe.discouragedWork).toEqual({ blockedEntryPaths: [], issueDiscoveryDiscouraged: false }); + expect(policy.publicSafe.labelExpectations).toEqual({ preferredLabels: [], linkedIssuePolicy: "optional" }); + expect(policy.publicSafe.validationExpectations).toEqual({ testExpectations: [], linkedIssueRequired: false, linkedIssuePreferred: false }); + expect(policy.publicSafe.entryGuidance).toEqual([]); + expect(policy.publicSafe.summary).toMatch(/unconstrained/i); + expect(policy.authenticated.readinessWarnings.length).toBeGreaterThan(0); + }); + + it("forwards parse warnings into authenticated.readinessWarnings for a malformed manifest", () => { + const policy = compileFocusManifestPolicy(parseFocusManifestContent("{ broken json")); + expect(policy.present).toBe(false); + expect(policy.authenticated.parseWarnings.join(" ")).toMatch(/not valid JSON/i); + expect(policy.authenticated.readinessWarnings.length).toBeGreaterThan(0); + }); + + // ── Typical: fully specified manifest ───────────────────────────────── + it("compiles a typical manifest into a complete policy schema", () => { + const manifest = parseFocusManifest({ + source: "repo_file", + wantedPaths: ["src/", "packages/*/lib"], + blockedPaths: ["migrations/", "infra/secrets.tf"], + preferredLabels: ["bug", "good first issue"], + linkedIssuePolicy: "required", + testExpectations: ["unit tests for new branches"], + issueDiscoveryPolicy: "discouraged", + maintainerNotes: ["Internal: ping @owner before the queue processor."], + publicNotes: ["Prefer small, focused PRs."], + }); + const policy = compileFocusManifestPolicy(manifest); + + expect(policy.present).toBe(true); + expect(policy.source).toBe("repo_file"); + + // contribution lanes + expect(policy.publicSafe.contributionLanes.directPrLane).toBe("preferred"); + expect(policy.publicSafe.contributionLanes.issueDiscoveryLane).toBe("discouraged"); + expect(policy.publicSafe.contributionLanes.preferredEntryPaths).toContain("src/"); + + // discouraged work + expect(policy.publicSafe.discouragedWork.blockedEntryPaths).toContain("migrations/"); + expect(policy.publicSafe.discouragedWork.issueDiscoveryDiscouraged).toBe(true); + + // label expectations + expect(policy.publicSafe.labelExpectations.preferredLabels).toContain("bug"); + expect(policy.publicSafe.labelExpectations.linkedIssuePolicy).toBe("required"); + + // validation expectations + expect(policy.publicSafe.validationExpectations.testExpectations).toContain("unit tests for new branches"); + expect(policy.publicSafe.validationExpectations.linkedIssueRequired).toBe(true); + expect(policy.publicSafe.validationExpectations.linkedIssuePreferred).toBe(false); + + // entry guidance + expect(policy.publicSafe.entryGuidance.join(" ")).toMatch(/src\/|bug|linked issue/i); + expect(policy.publicSafe.entryGuidance).toContain("Prefer small, focused PRs."); + + // summary + expect(policy.publicSafe.summary).toMatch(/wanted areas|preferred/i); + + // authenticated: maintainerNotes present and not in publicSafe + expect(policy.authenticated.maintainerContext).toContain("Internal: ping @owner before the queue processor."); + expect(JSON.stringify(policy.publicSafe)).not.toMatch(/ping @owner/); + }); + + // ── Missing-field: partial manifest ─────────────────────────────────── + it("handles a partial manifest with only linkedIssuePolicy set", () => { + const policy = compileFocusManifestPolicy(parseFocusManifest({ wantedPaths: ["src/"], linkedIssuePolicy: "preferred" })); + expect(policy.present).toBe(true); + expect(policy.publicSafe.contributionLanes.directPrLane).toBe("preferred"); + expect(policy.publicSafe.labelExpectations.linkedIssuePolicy).toBe("preferred"); + expect(policy.publicSafe.validationExpectations.linkedIssueRequired).toBe(false); + expect(policy.publicSafe.validationExpectations.linkedIssuePreferred).toBe(true); + expect(policy.publicSafe.discouragedWork.blockedEntryPaths).toEqual([]); + expect(policy.publicSafe.discouragedWork.issueDiscoveryDiscouraged).toBe(false); + expect(policy.authenticated.maintainerContext).toEqual([]); + }); + + it("handles a manifest with only issueDiscoveryPolicy:encouraged", () => { + const policy = compileFocusManifestPolicy(parseFocusManifest({ issueDiscoveryPolicy: "encouraged" })); + expect(policy.present).toBe(true); + expect(policy.publicSafe.contributionLanes.directPrLane).toBe("discouraged"); + expect(policy.publicSafe.contributionLanes.issueDiscoveryLane).toBe("preferred"); + expect(policy.publicSafe.discouragedWork.issueDiscoveryDiscouraged).toBe(false); + expect(policy.publicSafe.summary).toMatch(/issue.discovery is the preferred/i); + expect(policy.publicSafe.entryGuidance.join(" ")).toMatch(/welcomed|search for gaps/i); + }); + + it("handles a manifest with only blockedPaths set", () => { + const policy = compileFocusManifestPolicy(parseFocusManifest({ blockedPaths: ["infra/"] })); + expect(policy.present).toBe(true); + expect(policy.publicSafe.discouragedWork.blockedEntryPaths).toContain("infra/"); + expect(policy.publicSafe.contributionLanes.directPrLane).toBe("neutral"); + expect(policy.authenticated.readinessWarnings.join(" ")).toMatch(/blocked area/i); + }); + + it("emits a readiness warning when issue discovery is discouraged but no wanted paths are declared", () => { + const policy = compileFocusManifestPolicy(parseFocusManifest({ issueDiscoveryPolicy: "discouraged" })); + expect(policy.authenticated.readinessWarnings.join(" ")).toMatch(/limited guidance/i); + }); + + it("emits a readiness warning when linked issues are required but no labels or wanted paths guide contributors", () => { + const policy = compileFocusManifestPolicy(parseFocusManifest({ linkedIssuePolicy: "required" })); + expect(policy.authenticated.readinessWarnings.join(" ")).toMatch(/linked issues are required/i); + }); + + // ── Public/private separation ────────────────────────────────────────── + it("keeps maintainerContext out of publicSafe entirely", () => { + const policy = compileFocusManifestPolicy( + parseFocusManifest({ wantedPaths: ["src/"], maintainerNotes: ["Private queue note.", "Ping @owner privately."] }), + ); + expect(policy.authenticated.maintainerContext).toEqual(["Private queue note.", "Ping @owner privately."]); + expect(JSON.stringify(policy.publicSafe)).not.toMatch(/Private queue note|Ping @owner/); + }); + + it("excludes forbidden language from all publicSafe fields even when injected via publicNotes or testExpectations", () => { + const policy = compileFocusManifestPolicy( + parseFocusManifest({ + wantedPaths: ["src/"], + publicNotes: ["Maximize your reward payout", "Keep PRs focused."], + testExpectations: ["Submit wallet seed phrase proof", "npm run test:ci"], + }), + ); + const publicText = JSON.stringify(policy.publicSafe); + expect(publicText).not.toMatch(/reward payout|wallet seed/i); + expect(publicText).toContain("Keep PRs focused."); + expect(publicText).toContain("npm run test:ci"); + }); + + it("publicSafe.summary never contains forbidden language", () => { + const dangerous = parseFocusManifest({ wantedPaths: ["src/"], publicNotes: ["Boost your raw trust score here"] }); + const policy = compileFocusManifestPolicy(dangerous); + expect(isFocusManifestPublicSafe(policy.publicSafe.summary)).toBe(true); + }); + + it("preserves source field from the manifest", () => { + expect(compileFocusManifestPolicy(parseFocusManifest({ wantedPaths: ["src/"] }, "repo_file")).source).toBe("repo_file"); + expect(compileFocusManifestPolicy(parseFocusManifest({ wantedPaths: ["src/"] }, "api_record")).source).toBe("api_record"); + expect(compileFocusManifestPolicy(parseFocusManifest(null)).source).toBe("none"); + }); + + // ── Property-based sanitizer ─────────────────────────────────────────── + it("never emits forbidden language in any publicSafe field across random manifests", () => { + const stringPool = [ + "", + "src/", + "migrations/", + "Keep PRs focused.", + "Prefer small, focused PRs.", + "Maximize your reward payout", + "Internal: ping @owner", + "estimate your score", + "paste your hotkey", + "submit your wallet", + "npm run test:ci", + "packages/*/lib/*.ts", + ]; + const linkedIssuePolicies = ["required", "preferred", "optional"] as const; + const issueDiscoveryPolicies = ["encouraged", "neutral", "discouraged"] as const; + + let seed = 0xd4e3f2a1; + const next = () => { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; + return seed / 0x100000000; + }; + const pick = (items: readonly T[]): T => items[Math.floor(next() * items.length)] as T; + const sample = (max: number): string[] => + Array.from({ length: Math.floor(next() * (max + 1)) }, () => pick(stringPool)); + + for (let iteration = 0; iteration < 400; iteration += 1) { + const manifest = parseFocusManifest({ + wantedPaths: sample(4), + blockedPaths: sample(4), + preferredLabels: sample(4), + linkedIssuePolicy: pick(linkedIssuePolicies), + issueDiscoveryPolicy: pick(issueDiscoveryPolicies), + testExpectations: sample(3), + maintainerNotes: sample(4), + publicNotes: sample(4), + }); + const policy = compileFocusManifestPolicy(manifest); + const allPublicText = [ + ...policy.publicSafe.contributionLanes.preferredEntryPaths, + ...policy.publicSafe.discouragedWork.blockedEntryPaths, + ...policy.publicSafe.labelExpectations.preferredLabels, + ...policy.publicSafe.validationExpectations.testExpectations, + ...policy.publicSafe.entryGuidance, + policy.publicSafe.summary, + ]; + expect(allPublicText.every(isFocusManifestPublicSafe)).toBe(true); + } + }); +}); + describe("public-safe invariant", () => { it("rejects forbidden compensation/secret language", () => { expect(isFocusManifestPublicSafe("Keep PRs focused")).toBe(true);