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
124 changes: 124 additions & 0 deletions src/signals/focus-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,130 @@ function summarize(manifest: FocusManifest, blocked: string[], wanted: string[])
return "Maintainer focus manifest applied with no path-specific verdict.";
}

// ─── Contribution Lanes ──────────────────────────────────────────────────────

export type ContributionLanePreference = "preferred" | "neutral" | "discouraged";

/**
* Standalone contribution lane summary derived from a focus manifest.
* Does not require a specific change set — describes the general contribution
* policy the maintainer has declared so contributors and repo owners can plan
* their work before touching any files.
*/
export type ContributionLanes = {
present: boolean;
source: FocusManifestSource;
directPrLane: ContributionLanePreference;
issueDiscoveryLane: ContributionLanePreference;
preferredEntryPaths: string[];
discouragedEntryPaths: string[];
validationExpectations: string[];
issueEntryGuidance: string[];
prEntryGuidance: string[];
warnings: string[];
summary: string;
};

/**
* Derive contribution lanes from a focus manifest without requiring a specific
* change set. The result is deterministic, explainable, and public-safe: no
* maintainer-private notes, scoreability, reviewability, reward/risk, wallet,
* hotkey, or raw trust context appears in any output field.
*/
export function deriveContributionLanes(manifest: FocusManifest): ContributionLanes {
if (!manifest.present) {
return {
present: false,
source: manifest.source,
directPrLane: "neutral",
issueDiscoveryLane: "neutral",
preferredEntryPaths: [],
discouragedEntryPaths: [],
validationExpectations: [],
issueEntryGuidance: [],
prEntryGuidance: [],
warnings: manifest.warnings,
summary: "No maintainer focus manifest; contribution lanes are not constrained.",
};
}

const directPrLane = deriveDirectPrLane(manifest);
const issueDiscoveryLane = deriveIssueDiscoveryLane(manifest);
const validationExpectations = deriveValidationExpectations(manifest);
const issueEntryGuidance = deriveIssueEntryGuidance(manifest);
const prEntryGuidance = derivePrEntryGuidance(manifest);

return {
present: true,
source: manifest.source,
directPrLane,
issueDiscoveryLane,
preferredEntryPaths: manifest.wantedPaths.filter(isFocusManifestPublicSafe),
discouragedEntryPaths: manifest.blockedPaths.filter(isFocusManifestPublicSafe),
validationExpectations,
issueEntryGuidance,
prEntryGuidance,
warnings: manifest.warnings,
summary: lanesSummary(manifest, directPrLane, issueDiscoveryLane),
};
}

function deriveDirectPrLane(manifest: FocusManifest): ContributionLanePreference {
if (manifest.issueDiscoveryPolicy === "encouraged") return "discouraged";
if (manifest.wantedPaths.length > 0) return "preferred";
return "neutral";
}

function deriveIssueDiscoveryLane(manifest: FocusManifest): ContributionLanePreference {
if (manifest.issueDiscoveryPolicy === "encouraged") return "preferred";
if (manifest.issueDiscoveryPolicy === "discouraged") return "discouraged";
return "neutral";
}

function deriveValidationExpectations(manifest: FocusManifest): string[] {
const expectations: string[] = [];
if (manifest.linkedIssuePolicy === "required") expectations.push("Link a tracked issue before opening a PR.");
else if (manifest.linkedIssuePolicy === "preferred") expectations.push("Link a tracked issue if one exists.");
for (const expectation of manifest.testExpectations) {
if (isFocusManifestPublicSafe(expectation)) expectations.push(expectation);
}
return expectations;
}

function deriveIssueEntryGuidance(manifest: FocusManifest): string[] {
const guidance: string[] = [];
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; this repo discourages issue-discovery submissions.");
if (manifest.linkedIssuePolicy === "required") guidance.push("Issues must be linked to a PR before it is opened.");
else if (manifest.linkedIssuePolicy === "preferred") guidance.push("Link an existing issue to your PR when one is available.");
return guidance;
}

function derivePrEntryGuidance(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 to your PR: ${manifest.preferredLabels.slice(0, 3).join(", ")}.`);
}
for (const note of manifest.publicNotes) {
if (isFocusManifestPublicSafe(note)) guidance.push(note);
}
return [...new Set(guidance)].filter(isFocusManifestPublicSafe);
}

function lanesSummary(manifest: FocusManifest, directPrLane: ContributionLanePreference, issueDiscoveryLane: ContributionLanePreference): string {
if (issueDiscoveryLane === "preferred" && directPrLane === "discouraged") return "Issue-discovery is the preferred contribution mode for this repo.";
if (issueDiscoveryLane === "discouraged" && manifest.wantedPaths.length > 0) return "Direct PRs focused on the wanted areas are the preferred contribution mode.";
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 lanes are guided by the maintainer focus manifest.";
}

// ─── Focus Manifest Policy Schema ────────────────────────────────────────────

/** Preference signal for a contribution lane derived from the focus manifest. */
Expand Down
156 changes: 156 additions & 0 deletions test/unit/focus-manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import {
buildFocusManifestGuidance,
compileFocusManifestPolicy,
deriveContributionLanes,
isFocusManifestPublicSafe,
matchesManifestPath,
parseFocusManifest,
Expand Down Expand Up @@ -430,6 +431,161 @@ describe("compileFocusManifestPolicy", () => {
});
});

describe("deriveContributionLanes", () => {
it("returns neutral lanes with no constraints when no manifest is present", () => {
const lanes = deriveContributionLanes(parseFocusManifest(null));
expect(lanes.present).toBe(false);
expect(lanes.directPrLane).toBe("neutral");
expect(lanes.issueDiscoveryLane).toBe("neutral");
expect(lanes.preferredEntryPaths).toEqual([]);
expect(lanes.discouragedEntryPaths).toEqual([]);
expect(lanes.validationExpectations).toEqual([]);
expect(lanes.issueEntryGuidance).toEqual([]);
expect(lanes.prEntryGuidance).toEqual([]);
expect(lanes.summary).toMatch(/not constrained/i);
});

it("marks direct-PR as preferred when wanted paths are declared", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/", "lib/"] }));
expect(lanes.present).toBe(true);
expect(lanes.directPrLane).toBe("preferred");
expect(lanes.issueDiscoveryLane).toBe("neutral");
expect(lanes.preferredEntryPaths).toEqual(["src/", "lib/"]);
expect(lanes.prEntryGuidance.join(" ")).toMatch(/src\//);
expect(lanes.summary).toMatch(/wanted areas are preferred/i);
});

it("marks issue-discovery as preferred and direct-PR as discouraged when issueDiscoveryPolicy is encouraged", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ issueDiscoveryPolicy: "encouraged" }));
expect(lanes.directPrLane).toBe("discouraged");
expect(lanes.issueDiscoveryLane).toBe("preferred");
expect(lanes.issueEntryGuidance.join(" ")).toMatch(/welcomed|search for gaps/i);
expect(lanes.summary).toMatch(/issue.discovery is the preferred/i);
});

it("marks issue-discovery as discouraged when issueDiscoveryPolicy is discouraged", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/"], issueDiscoveryPolicy: "discouraged" }));
expect(lanes.issueDiscoveryLane).toBe("discouraged");
expect(lanes.directPrLane).toBe("preferred");
expect(lanes.issueEntryGuidance.join(" ")).toMatch(/prefer direct fixes|discourages/i);
expect(lanes.summary).toMatch(/wanted areas are the preferred/i);
});

it("surfaces validation expectations from testExpectations and linkedIssuePolicy", () => {
const lanes = deriveContributionLanes(
parseFocusManifest({ wantedPaths: ["src/"], linkedIssuePolicy: "required", testExpectations: ["unit tests for new branches", "npm run test:ci"] }),
);
expect(lanes.validationExpectations).toContain("Link a tracked issue before opening a PR.");
expect(lanes.validationExpectations).toContain("unit tests for new branches");
expect(lanes.validationExpectations).toContain("npm run test:ci");
});

it("produces preferred validation hint for linkedIssuePolicy:preferred", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/"], linkedIssuePolicy: "preferred" }));
expect(lanes.validationExpectations).toContain("Link a tracked issue if one exists.");
expect(lanes.issueEntryGuidance).toContain("Link an existing issue to your PR when one is available.");
});

it("includes required link requirement in both validation expectations and issue entry guidance", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/"], linkedIssuePolicy: "required" }));
expect(lanes.validationExpectations).toContain("Link a tracked issue before opening a PR.");
expect(lanes.issueEntryGuidance).toContain("Issues must be linked to a PR before it is opened.");
});

it("includes blocked paths in discouragedEntryPaths and PR entry guidance", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/"], blockedPaths: ["migrations/", "infra/secrets.tf"] }));
expect(lanes.discouragedEntryPaths).toEqual(["migrations/", "infra/secrets.tf"]);
expect(lanes.prEntryGuidance.join(" ")).toMatch(/migrations\/.*infra\/secrets\.tf|infra\/secrets\.tf.*migrations\//);
});

it("includes preferred labels in PR entry guidance", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/"], preferredLabels: ["bug", "good first issue"] }));
expect(lanes.prEntryGuidance.join(" ")).toMatch(/bug|good first issue/);
});

it("includes maintainer public notes in PR entry guidance", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/"], publicNotes: ["Prefer small, focused PRs."] }));
expect(lanes.prEntryGuidance).toContain("Prefer small, focused PRs.");
});

it("excludes maintainerNotes from all output fields", () => {
const lanes = deriveContributionLanes(
parseFocusManifest({ wantedPaths: ["src/"], maintainerNotes: ["Internal: ping @owner before touching the queue processor."] }),
);
const serialized = JSON.stringify(lanes);
expect(serialized).not.toMatch(/ping @owner/);
expect(serialized).not.toMatch(/Internal:/);
});

it("filters public notes containing forbidden language before including them in prEntryGuidance", () => {
const lanes = deriveContributionLanes(
parseFocusManifest({ wantedPaths: ["src/"], publicNotes: ["Maximize your reward payout", "Keep PRs focused."] }),
);
expect(lanes.prEntryGuidance).not.toContain("Maximize your reward payout");
expect(lanes.prEntryGuidance).toContain("Keep PRs focused.");
});

it("filters testExpectations containing forbidden language before including them in validationExpectations", () => {
const lanes = deriveContributionLanes(
parseFocusManifest({ wantedPaths: ["src/"], testExpectations: ["Submit your wallet seed phrase", "npm run test:ci"] }),
);
expect(lanes.validationExpectations).not.toContain("Submit your wallet seed phrase");
expect(lanes.validationExpectations).toContain("npm run test:ci");
});

it("preserves source from the manifest", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ wantedPaths: ["src/"] }, "repo_file"));
expect(lanes.source).toBe("repo_file");
});

it("passes a comprehensive manifest fixture end-to-end with all fields populated", () => {
const manifest = parseFocusManifest({
source: "repo_file",
wantedPaths: ["src/", "packages/*/lib"],
blockedPaths: ["migrations/"],
preferredLabels: ["bug", "good first issue"],
linkedIssuePolicy: "required",
testExpectations: ["unit tests for new branches"],
issueDiscoveryPolicy: "discouraged",
maintainerNotes: ["Internal: ping @owner"],
publicNotes: ["Prefer small, focused PRs."],
});
const lanes = deriveContributionLanes(manifest);

expect(lanes.present).toBe(true);
expect(lanes.source).toBe("repo_file");
expect(lanes.directPrLane).toBe("preferred");
expect(lanes.issueDiscoveryLane).toBe("discouraged");
expect(lanes.preferredEntryPaths).toContain("src/");
expect(lanes.discouragedEntryPaths).toContain("migrations/");
expect(lanes.validationExpectations).toContain("Link a tracked issue before opening a PR.");
expect(lanes.validationExpectations).toContain("unit tests for new branches");
expect(lanes.issueEntryGuidance.join(" ")).toMatch(/discourages/i);
expect(lanes.prEntryGuidance.join(" ")).toMatch(/bug|good first issue/i);
expect(lanes.prEntryGuidance).toContain("Prefer small, focused PRs.");
expect(lanes.summary).toMatch(/wanted areas/i);

const serialized = JSON.stringify(lanes);
expect(serialized).not.toMatch(/ping @owner/);
expect(serialized).not.toMatch(/\b(wallet|hotkey|coldkey|raw trust|trust score|payout|reward|farming|private reviewability)\b/i);
});

it("keeps both lanes neutral with a default summary when a present manifest declares no wanted paths or policies", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ preferredLabels: ["bug"] }));
expect(lanes.present).toBe(true);
expect(lanes.directPrLane).toBe("neutral");
expect(lanes.issueDiscoveryLane).toBe("neutral");
expect(lanes.summary).toMatch(/guided by the maintainer focus manifest/i);
});

it("recommends direct PRs when issue-discovery is discouraged without any wanted paths", () => {
const lanes = deriveContributionLanes(parseFocusManifest({ issueDiscoveryPolicy: "discouraged", preferredLabels: ["bug"] }));
expect(lanes.directPrLane).toBe("neutral");
expect(lanes.issueDiscoveryLane).toBe("discouraged");
expect(lanes.summary).toMatch(/direct prs are preferred; issue-discovery submissions are discouraged/i);
});
});

describe("public-safe invariant", () => {
it("rejects forbidden compensation/secret language", () => {
expect(isFocusManifestPublicSafe("Keep PRs focused")).toBe(true);
Expand Down