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: 5 additions & 1 deletion src/signals/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4953,7 +4953,11 @@ export function buildPrTextLint(input: PrTextLintInput): PrTextLintReport {
// Exported so the deterministic no-linked-issue slop signal (#562) and the public PR-panel traceability check
// share ONE definition of a "clear no-issue rationale" (maintenance / docs-only / "no issue: …" in the PR text).
export function hasClearNoIssueRationale(pr: Pick<PullRequestRecord, "title" | "body">): boolean {
return /\b(?:no issue\s*(?:because\b|:)|no linked issue\s*(?:because\b|:)|no ticket\s*(?:because\b|:)|(?:maintenance|docs? only|typo|chore|cleanup)\b)/i.test([pr.title, pr.body ?? ""].join(" "));
// `docs?[\s-]+only` matches the space form ("docs only") AND the hyphenated "docs-only" / "doc-only"
// spelling this function's own docstring uses — the dominant GitHub/Conventional-Commits form. A bare
// `docs? only` missed the hyphen, so a docs-only PR with no linked issue was wrongly denied a clear
// no-issue rationale and hard-blocked under `linkedIssueGateMode === "block"`.
return /\b(?:no issue\s*(?:because\b|:)|no linked issue\s*(?:because\b|:)|no ticket\s*(?:because\b|:)|(?:maintenance|docs?[\s-]+only|typo|chore|cleanup)\b)/i.test([pr.title, pr.body ?? ""].join(" "));
}

function hasValidationNote(value: string): boolean {
Expand Down
18 changes: 18 additions & 0 deletions test/unit/signals-v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
buildRegistryChangeReport,
buildRepoFitRecommendation,
buildRoleContext,
hasClearNoIssueRationale,
type ContributorFit,
type ContributorOutcomeHistory,
type ContributorScoringProfile,
Expand Down Expand Up @@ -1710,6 +1711,23 @@ describe("v2 signal builders", () => {
});
});

describe("hasClearNoIssueRationale docs-only spelling", () => {
it("recognizes the hyphenated docs-only rationale, not only the space form", () => {
// The space form already worked; these hyphenated forms (the dominant GitHub / Conventional-Commits
// spelling, and the one this function's own docstring uses) were wrongly missed, hard-blocking a
// docs-only PR with no linked issue under linkedIssueGateMode === "block".
expect(hasClearNoIssueRationale({ title: "docs only: clarify README", body: "" })).toBe(true);
expect(hasClearNoIssueRationale({ title: "docs-only: clarify README", body: "" })).toBe(true);
expect(hasClearNoIssueRationale({ title: "doc-only update", body: "" })).toBe(true);
expect(hasClearNoIssueRationale({ title: "Improve install steps", body: "This is a docs-only change." })).toBe(true);
});

it("still rejects PR text with no clear no-issue rationale", () => {
expect(hasClearNoIssueRationale({ title: "Improve install steps", body: "Adds a new option." })).toBe(false);
expect(hasClearNoIssueRationale({ title: "Add documentation site", body: "" })).toBe(false);
});
});

function snapshot(
id: string,
repositories: Array<{
Expand Down
Loading