Skip to content

Commit 8cd659c

Browse files
committed
fix(ci): soft-pass detailed issues after maintainer retitle
Issue #545 was auto-closed when a maintainer retitle dropped the [Bug]: prefix; soft-pass no longer requires the title prefix once kind is already established and the body has substantial structured content.
1 parent 0fa45cf commit 8cd659c

2 files changed

Lines changed: 76 additions & 9 deletions

File tree

.github/scripts/issue-quality.cjs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,6 @@ function validateIssue(issue) {
541541
const reasons = [];
542542
const guidance = [];
543543
let softPass = false;
544-
const titleLower = title.toLowerCase();
545544

546545
if (!kind) {
547546
const failure = untemplatedIssueFailure(issue);
@@ -578,9 +577,11 @@ function validateIssue(issue) {
578577
goal !== null || blocker !== null || behaviour !== null || example !== null;
579578

580579
if (emptyCore.length > 0) {
580+
// Soft-pass rich non-template bodies once kind is already feature (title
581+
// prefix, enhancement label, or stored kind). Do not require the title to
582+
// keep a `[Feature]:` prefix — maintainer retitles must not re-arm closure.
581583
const canSoftPass =
582584
!mappedHeadingPresent &&
583-
titleLower.startsWith("[feature]:") &&
584585
hasSubstantialStructuredContent(body);
585586
if (canSoftPass) {
586587
softPass = true;
@@ -625,10 +626,14 @@ function validateIssue(issue) {
625626
const os = extractSection(body, "Operating system") ?? extractSection(body, "OS");
626627

627628
if (isEmpty(summary) && isEmpty(repro)) {
629+
// Soft-pass substantial non-English / freeform structured reports once
630+
// kind is already bug (label, stored kind, or prior `[Bug]:` detection).
631+
// Requiring the title to keep a `[Bug]:` prefix caused #545: a maintainer
632+
// retitle of an already detailed report was treated as empty Summary/
633+
// Reproduction and auto-closed.
628634
const canSoftPass =
629635
summary === null &&
630636
repro === null &&
631-
titleLower.startsWith("[bug]:") &&
632637
hasSubstantialStructuredContent(body);
633638
if (canSoftPass) {
634639
softPass = true;
@@ -687,27 +692,38 @@ function validateIssue(issue) {
687692
if (version !== null && isRawPlaceholder(version) === false && isEmpty(version)) emptyCore.push("OpenCodex version");
688693
if (endpoint !== null && isEmpty(endpoint)) emptyCore.push("endpoint or capability");
689694
if (emptyCore.length > 0) {
690-
reasons.push(`Required sections are missing or empty: ${emptyCore.join(", ")}.`);
691-
guidance.push("Describe both the current and expected behaviour.");
695+
// Same soft-pass as bug/feature: label- or maintainer-scoped provider
696+
// reports often use non-English structured headings after a retitle.
697+
const mappedHeadingPresent =
698+
current !== null || expected !== null || repro !== null || response !== null || docs !== null;
699+
const canSoftPass =
700+
!mappedHeadingPresent &&
701+
hasSubstantialStructuredContent(body);
702+
if (canSoftPass) {
703+
softPass = true;
704+
} else {
705+
reasons.push(`Required sections are missing or empty: ${emptyCore.join(", ")}.`);
706+
guidance.push("Describe both the current and expected behaviour.");
707+
}
692708
}
693709

694-
if (!isEmpty(current) && !isEmpty(expected) && canonicalise(current) === canonicalise(expected)) {
710+
if (!softPass && !isEmpty(current) && !isEmpty(expected) && canonicalise(current) === canonicalise(expected)) {
695711
reasons.push("Current and expected behaviour are effectively identical.");
696712
guidance.push("Explain the difference between what happens now and what should happen.");
697713
}
698714

699715
const allSections = [current, expected, repro, response].filter((s) => !isEmpty(s));
700-
if (allSections.length >= 2 && allRepeatTitle(allSections, title)) {
716+
if (!softPass && allSections.length >= 2 && allRepeatTitle(allSections, title)) {
701717
reasons.push("All sections merely repeat the issue title.");
702718
guidance.push("Add specific detail in each section.");
703719
}
704720

705-
if (isEmpty(repro) && isEmpty(response)) {
721+
if (!softPass && isEmpty(repro) && isEmpty(response)) {
706722
reasons.push("Both the request/reproduction and the actual response/error are absent.");
707723
guidance.push("Include at least a minimal redacted request or the actual error output.");
708724
}
709725

710-
if (isEmpty(docs)) {
726+
if (!softPass && isEmpty(docs)) {
711727
reasons.push("Upstream documentation is empty without stating that no public specification exists.");
712728
guidance.push("Add a URL to the provider specification, or state that no public spec exists.");
713729
}

.github/scripts/issue-quality.test.cjs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,57 @@ describe("translated feature headings and soft-pass", () => {
998998
assert.equal(result.valid, false);
999999
});
10001000

1001+
it("soft-passes retitled feature reports that drop the [Feature]: prefix", () => {
1002+
const body = [
1003+
"### Concrete user workflow that fails",
1004+
"User pastes an image in Codex App while a text-only routed model is selected and the App blocks upload.",
1005+
"### Why this matters",
1006+
"Vision sidecar is advertised but never reached from the App client path.",
1007+
"### Verification",
1008+
"Same proxy config works end-to-end in Claude Code with the sidecar describing the image.",
1009+
].join("\n");
1010+
const result = validateIssue({
1011+
title: "Vision sidecar unusable from Codex App",
1012+
body,
1013+
labels: ["enhancement"],
1014+
storedKind: "feature",
1015+
});
1016+
assert.equal(result.kind, "feature");
1017+
assert.equal(result.softPass, true);
1018+
});
1019+
1020+
it("soft-passes retitled bug reports with substantial non-English structure (#545)", () => {
1021+
// Maintainer retitle removed `[Bug]:`; Korean structured body has no English
1022+
// Summary/Reproduction headings but is clearly actionable.
1023+
const body = [
1024+
"## 환경",
1025+
"- opencodex 2.7.41 (launchd, port 10100)",
1026+
"- Claude Desktop 3P + Anthropic OAuth (Pro/Max)",
1027+
"- Auto Mode classifier model = `claude-sonnet-5`",
1028+
"",
1029+
"## 증상",
1030+
"Auto Mode classifier requests truncate at outputTokens=64 with max_output_tokens,",
1031+
"then retry the same payload up to 5 times. Dashboard previously showed 502.",
1032+
"",
1033+
"## 재현",
1034+
"1. `ocx login anthropic` and enable Claude Desktop 3P gateway key mode",
1035+
"2. Enable Auto Mode and trigger a tool permission classifier turn",
1036+
"3. Observe five identical 64-token incomplete terminals for one approval",
1037+
"",
1038+
"## 증거",
1039+
"Inbound+outbound correlated captures show max_tokens:64 and stop_sequences preserved.",
1040+
].join("\n");
1041+
const result = validateIssue({
1042+
title: "Claude Desktop 3P Auto Mode classifier retries after 64-token Anthropic OAuth outputs",
1043+
body,
1044+
labels: ["bug", "provider-compatibility"],
1045+
storedKind: "bug",
1046+
});
1047+
assert.equal(result.kind, "bug");
1048+
assert.equal(result.softPass, true, `Expected soft-pass but got: ${result.reasons.join("; ")}`);
1049+
assert.equal(result.valid, false);
1050+
});
1051+
10011052
it("still rejects empty [Feature]: bodies", () => {
10021053
const result = validateIssue({
10031054
title: "[Feature]: do something cool",

0 commit comments

Comments
 (0)