From 1ead98e3f3914f71f92c36ba08ee6e93fe24b13b Mon Sep 17 00:00:00 2001 From: ultrahighsuper Date: Tue, 30 Jun 2026 15:22:30 -0500 Subject: [PATCH] fix(signals): match hyphenated docs-only no-issue rationale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hasClearNoIssueRationale matched only the space form "docs only" via `docs? only`, missing the hyphenated "docs-only" / "doc-only" spelling — the dominant GitHub / Conventional-Commits form, and the one this function's own docstring uses. A docs-only PR with no linked issue was therefore denied a clear no-issue rationale and hard-blocked under linkedIssueGateMode === "block" (the gate then auto-closes it). Widen the alternative to `docs?[\s-]+only` and cover the hyphenated, embedded, and negative cases. --- src/signals/engine.ts | 6 +++++- test/unit/signals-v2.test.ts | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/signals/engine.ts b/src/signals/engine.ts index a8af3c19f5..2ced7adc7d 100644 --- a/src/signals/engine.ts +++ b/src/signals/engine.ts @@ -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): 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 { diff --git a/test/unit/signals-v2.test.ts b/test/unit/signals-v2.test.ts index b6f6e22caa..1f1a44f0d7 100644 --- a/test/unit/signals-v2.test.ts +++ b/test/unit/signals-v2.test.ts @@ -26,6 +26,7 @@ import { buildRegistryChangeReport, buildRepoFitRecommendation, buildRoleContext, + hasClearNoIssueRationale, type ContributorFit, type ContributorOutcomeHistory, type ContributorScoringProfile, @@ -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<{