From 71e47b72bef964e8a82d06de2a936794a326eec8 Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:29:06 +0100 Subject: [PATCH] Preserve visible issue fields after pasted HTML code Ignore HTML comment delimiters inside the fenced and indented code already excluded from issue declarations. An unfinished copied response must not erase the actual failing-version, feedback or additional-topic fields that follow it. Keep genuine hidden comments hidden, preserve community labels and verify the existing GitHub label consumer without posting or closing reports. Contract-Neutral: Restore existing visible-declaration intake semantics only; no Pulse runtime, public API, workflow identity, contact, disposition or release-policy delta. Change-source: pulse-maintainer --- .github/scripts/issue-version-triage.cjs | 42 +++++++++++- .github/scripts/issue-version-triage.test.cjs | 67 +++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/.github/scripts/issue-version-triage.cjs b/.github/scripts/issue-version-triage.cjs index 4eee3e6027..bd4d9539b3 100644 --- a/.github/scripts/issue-version-triage.cjs +++ b/.github/scripts/issue-version-triage.cjs @@ -59,7 +59,47 @@ function stripHTMLComments(value) { let previous; do { previous = stripped; - stripped = stripped.replace(/|$)/g, ""); + let fence = null; + let inComment = false; + // Strip hidden template text, not literal markers in pasted code. In + // particular, an unfinished HTML response must not swallow the real form + // fields after its closing Markdown fence. Match the same fences and + // indentation that declarationLines excludes from metadata declarations. + stripped = stripped.split(/(\r?\n)/).map((text, index) => { + if (index % 2) return text; + const marker = text.match(/^ {0,3}(`{3,}|~{3,})(.*)$/); + if (!inComment) { + if (fence) { + if (marker && marker[1][0] === fence.character && + marker[1].length >= fence.length && !marker[2].trim()) { + fence = null; + } + return text; + } + if (marker && (marker[1][0] === "~" || !marker[2].includes("`"))) { + fence = { character: marker[1][0], length: marker[1].length }; + return text; + } + if (/^(?: {4}|\t)/.test(text)) return text; + } + let visible = ""; + let offset = 0; + while (offset < text.length) { + if (inComment) { + const end = text.indexOf("-->", offset); + if (end === -1) break; + offset = end + 3; + inComment = false; + } else { + const start = text.indexOf("", "-->"]) { + const body = [open, marker, "### Pulse version", "6.4.1", close, "", + "### Pulse version", "6.5.0", "### Feedback type", "Documentation issue", + "### Additional actionable topics", "Missing disk reading"].join(newline); + assert.equal(extractPulseVersion("Upgrade from v6.4.5", body), "6.5.0", body); + assert.equal(classifyV6FeedbackType(body), "documentation", body); + assert.equal(classifyAdditionalActionableTopics(body), true, body); + } + } + for (const indent of [" ", "\t"]) { + const body = [`${indent}", ...visible].join(newline); + assert.equal(extractPulseVersion("Upgrade from v6.4.5", closed), "6.5.0"); + assert.equal(classifyV6FeedbackType(closed), "documentation"); + assert.equal(classifyAdditionalActionableTopics(closed), false); + const unfinished = [...hidden, ...visible].join(newline); + assert.equal(extractPulseVersion("Bug", unfinished), null); + assert.equal(classifyV6FeedbackType(unfinished), null); + assert.equal(classifyAdditionalActionableTopics(unfinished), null); + } +}); + +test("pasted HTML keeps genuine topic values without turning copied headings into fields", () => { + const body = ["### Additional actionable topics", "```html", "", "None\n\n"]) { assert.equal(triage.internals.classifyAdditionalActionableTopics(