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
16 changes: 15 additions & 1 deletion .github/scripts/issue-quality.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,10 @@ function detectIssueKind(issue) {
// ---------------------------------------------------------------------------

function isEmpty(text) {
return clean(text).length === 0;
const c = clean(text);
if (c.length === 0) return true;
// Stand-ins like "...", "…", "---" are not actionable report content.
return /^[\p{P}\p{S}\s]+$/u.test(c);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict empty detection to known punctuation placeholders

This pattern classifies every punctuation- or symbol-only value as empty, not just stand-ins such as ... and ---. A concrete reproduction consisting of the problematic payload {}, [], /, or even an emoji is therefore rejected with Reproduction is empty; the enforcement path in .github/workflows/enforce-issue-quality.yml then closes the report. Match only known placeholder forms (such as ellipses or repeated dashes) rather than all Unicode punctuation and symbols.

Useful? React with 👍 / 👎.

}

function allSameCanonical(sections) {
Expand Down Expand Up @@ -642,6 +645,17 @@ function validateIssue(issue) {
reasons.push("Both Summary and Reproduction are empty.");
guidance.push("Describe what happened and how to reproduce it.");
}
} else {
// Each mapped field is required on its own — a filled Summary with an
// empty / ellipsis Reproduction (e.g. #598) must not pass.
if (isEmpty(summary)) {
reasons.push("Summary is empty.");
guidance.push("Describe what happened (the symptom or error).");
}
if (isEmpty(repro)) {
reasons.push("Reproduction is empty.");
guidance.push("List the exact steps to reproduce the problem.");
}
}

// Required environment fields removed after submission.
Expand Down
68 changes: 66 additions & 2 deletions .github/scripts/issue-quality.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,8 @@ describe("validateIssue - feature", () => {
assert.equal(result.kind, "feature");
assert.equal(result.valid, false, `Expected terse goal "${goal}" to be invalid`);
assert.ok(
result.reasons.some((r) => r.includes("too vague")),
`Expected too vague reason for "${goal}", got: ${result.reasons.join(", ")}`,
result.reasons.some((r) => r.includes("too vague") || /missing or empty/i.test(r)),
`Expected too vague or empty reason for "${goal}", got: ${result.reasons.join(", ")}`,
);
}
});
Expand Down Expand Up @@ -536,6 +536,70 @@ describe("validateIssue - bug", () => {
assert.equal(result.valid, false);
});

it("rejects a bug with Summary filled but Reproduction empty", () => {
const body = [
"### Client or integration",
"Codex CLI",
"### Area",
"CLI",
"### Summary",
"The 'gpt-5.6-sol' model is not supported when using Codex with a ChatGPT account.",
"### Reproduction",
"No response",
"### Version",
"2.7.42",
"### Operating system",
"macOS",
].join("\n");
const result = validateIssue({ title: "Open Codex Error", body, labels: ["bug"] });
assert.equal(result.kind, "bug");
assert.equal(result.valid, false);
assert.ok(result.reasons.some((r) => /Reproduction is empty/i.test(r)));
assert.ok(!result.reasons.some((r) => /Summary is empty/i.test(r)));
});

it("rejects a bug whose Reproduction is only an ellipsis (#598)", () => {
const body = [
"### Client or integration",
"Codex CLI",
"### Area",
"CLI",
"### Summary",
"{\"detail\":\"The 'gpt-5.6-sol' model is not supported when using Codex with a ChatGPT account.\"}",
"### Reproduction",
"...",
"### Version",
"2.7.42",
"### Operating system",
"mac os",
].join("\n");
const result = validateIssue({ title: "Open Codex Error", body, labels: ["bug"] });
assert.equal(result.kind, "bug");
assert.equal(result.valid, false);
assert.ok(result.reasons.some((r) => /Reproduction is empty/i.test(r)));
});

it("rejects a bug with Reproduction filled but Summary empty", () => {
const body = [
"### Client or integration",
"Codex CLI",
"### Area",
"CLI",
"### Summary",
"",
"### Reproduction",
"1. Run ocx start\n2. Send a request",
"### Version",
"2.7.42",
"### Operating system",
"macOS",
].join("\n");
const result = validateIssue({ title: "Crash", body, labels: ["bug"] });
assert.equal(result.kind, "bug");
assert.equal(result.valid, false);
assert.ok(result.reasons.some((r) => /Summary is empty/i.test(r)));
});

it("accepts a terse real crash report", () => {
const body = [
"### Client or integration",
Expand Down
Loading