From 10b75e1fbb0bfc339fc986633cf880a36b08d744 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:48:00 +0000 Subject: [PATCH 1/7] chore: plan comment-memory code-fence update Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c58aea4c-9179-42ff-b29b-9bbb205e5f9b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/spec_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/cli/spec_test.go b/pkg/cli/spec_test.go index c6b739b93fc..f46eabc88ec 100644 --- a/pkg/cli/spec_test.go +++ b/pkg/cli/spec_test.go @@ -1117,11 +1117,11 @@ func TestSpec_PublicAPI_ValidateWorkflowIntent(t *testing.T) { // Spec: "Sets a field in frontmatter YAML" func TestSpec_PublicAPI_UpdateFieldInFrontmatter(t *testing.T) { tests := []struct { - name string - content string - fieldName string - fieldValue string - wantErr bool + name string + content string + fieldName string + fieldValue string + wantErr bool checkContains string }{ { From 66712862e6e4083df6289c9351442f2120da13f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:52:10 +0000 Subject: [PATCH 2/7] fix(comment-memory): wrap managed memory content in six-backtick code region Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c58aea4c-9179-42ff-b29b-9bbb205e5f9b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/comment_memory.cjs | 4 +-- actions/setup/js/comment_memory.test.cjs | 2 ++ actions/setup/js/comment_memory_helpers.cjs | 29 ++++++++++++++++++- .../setup/js/comment_memory_helpers.test.cjs | 5 ++++ .../js/setup_comment_memory_files.test.cjs | 10 +++---- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/actions/setup/js/comment_memory.cjs b/actions/setup/js/comment_memory.cjs index 81889b6a90a..002693aea28 100644 --- a/actions/setup/js/comment_memory.cjs +++ b/actions/setup/js/comment_memory.cjs @@ -14,7 +14,7 @@ const { buildWorkflowRunUrl } = require("./workflow_metadata_helpers.cjs"); const { getTrackerID } = require("./get_tracker_id.cjs"); const { generateHistoryUrl } = require("./generate_history_link.cjs"); const { enforceCommentLimits } = require("./comment_limit_helpers.cjs"); -const { COMMENT_MEMORY_TAG, COMMENT_MEMORY_MAX_SCAN_PAGES } = require("./comment_memory_helpers.cjs"); +const { COMMENT_MEMORY_TAG, COMMENT_MEMORY_MAX_SCAN_PAGES, COMMENT_MEMORY_CODE_FENCE } = require("./comment_memory_helpers.cjs"); // Require provenance marker to avoid accidentally updating user-authored comments // that happen to contain a matching comment-memory tag. const MANAGED_COMMENT_PROVENANCE_MARKER = ""; const COMMENT_MEMORY_PROMPT_END_MARKER = ""; +const COMMENT_MEMORY_CODE_FENCE = "``````"; + +function stripCommentMemoryCodeFence(content) { + const trimmed = String(content || "").trim(); + if (!trimmed.startsWith(COMMENT_MEMORY_CODE_FENCE)) { + return trimmed; + } + + const firstNewline = trimmed.indexOf("\n", COMMENT_MEMORY_CODE_FENCE.length); + if (firstNewline < 0) { + return trimmed; + } + + const closingFenceStart = trimmed.lastIndexOf(`\n${COMMENT_MEMORY_CODE_FENCE}`); + if (closingFenceStart <= firstNewline) { + return trimmed; + } + + const trailing = trimmed.slice(closingFenceStart + COMMENT_MEMORY_CODE_FENCE.length + 1).trim(); + if (trailing.length > 0) { + return trimmed; + } + + return trimmed.slice(firstNewline + 1, closingFenceStart).trim(); +} function isSafeMemoryId(memoryId) { if (typeof memoryId !== "string" || memoryId.length === 0 || memoryId.length > MAX_MEMORY_ID_LENGTH) { @@ -57,7 +82,7 @@ function extractCommentMemoryEntries(commentBody, warn = () => {}) { if (isSafeMemoryId(memoryId)) { entries.push({ memoryId, - content: (commentBody.slice(contentStart, closeStart) || "").trim(), + content: stripCommentMemoryCodeFence(commentBody.slice(contentStart, closeStart)), }); } else { warn(`skipping unsafe memory_id '${memoryId}'`); @@ -99,7 +124,9 @@ module.exports = { COMMENT_MEMORY_MAX_SCAN_EMPTY_PAGES, COMMENT_MEMORY_PROMPT_START_MARKER, COMMENT_MEMORY_PROMPT_END_MARKER, + COMMENT_MEMORY_CODE_FENCE, isSafeMemoryId, + stripCommentMemoryCodeFence, extractCommentMemoryEntries, listCommentMemoryFiles, resolveCommentMemoryConfig, diff --git a/actions/setup/js/comment_memory_helpers.test.cjs b/actions/setup/js/comment_memory_helpers.test.cjs index 0c7ef0d79a2..5b19b5ebcd6 100644 --- a/actions/setup/js/comment_memory_helpers.test.cjs +++ b/actions/setup/js/comment_memory_helpers.test.cjs @@ -3,6 +3,11 @@ import { extractCommentMemoryEntries, isSafeMemoryId } from "./comment_memory_he describe("comment_memory_helpers", () => { it("extracts managed memory entries", () => { + const entries = extractCommentMemoryEntries('\n``````\nhello\n``````\n'); + expect(entries).toEqual([{ memoryId: "default", content: "hello" }]); + }); + + it("supports legacy memory entries without code fence markers", () => { const entries = extractCommentMemoryEntries('\nhello\n'); expect(entries).toEqual([{ memoryId: "default", content: "hello" }]); }); diff --git a/actions/setup/js/setup_comment_memory_files.test.cjs b/actions/setup/js/setup_comment_memory_files.test.cjs index 1580880cf30..be2bf764834 100644 --- a/actions/setup/js/setup_comment_memory_files.test.cjs +++ b/actions/setup/js/setup_comment_memory_files.test.cjs @@ -35,7 +35,7 @@ describe("setup_comment_memory_files", () => { it("extracts memory entries from managed comment body", async () => { const module = await import("./setup_comment_memory_files.cjs"); - const entries = module.extractCommentMemoryEntries('\nhello\n'); + const entries = module.extractCommentMemoryEntries('\n``````\nhello\n``````\n'); expect(entries).toEqual([{ memoryId: "default", content: "hello" }]); }); @@ -47,7 +47,7 @@ describe("setup_comment_memory_files", () => { listComments: vi.fn().mockResolvedValue({ data: [ { - body: '\nSaved memory\n\nfooter', + body: '\n``````\nSaved memory\n``````\n\nfooter', }, ], }), @@ -76,7 +76,7 @@ describe("setup_comment_memory_files", () => { }); } if (page === 6) { - return Promise.resolve({ data: [{ body: '\nLate memory\n' }] }); + return Promise.resolve({ data: [{ body: '\n``````\nLate memory\n``````\n' }] }); } return Promise.resolve({ data: [] }); }); @@ -156,7 +156,7 @@ describe("setup_comment_memory_files", () => { }) ); const listComments = vi.fn().mockResolvedValue({ - data: [{ body: '\nCross repo memory\n' }], + data: [{ body: '\n``````\nCross repo memory\n``````\n' }], }); global.github = { rest: { @@ -192,7 +192,7 @@ describe("setup_comment_memory_files", () => { }) ); const listComments = vi.fn().mockResolvedValue({ - data: [{ body: '\nSame repo memory\n' }], + data: [{ body: '\n``````\nSame repo memory\n``````\n' }], }); global.github = { rest: { From 70b6bb7f836b3b58360a21e4fad01cb257754a88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:58:06 +0000 Subject: [PATCH 3/7] refactor(comment-memory): simplify managed body template string Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c58aea4c-9179-42ff-b29b-9bbb205e5f9b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/comment_memory.cjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/actions/setup/js/comment_memory.cjs b/actions/setup/js/comment_memory.cjs index 002693aea28..d4a3fb49927 100644 --- a/actions/setup/js/comment_memory.cjs +++ b/actions/setup/js/comment_memory.cjs @@ -45,7 +45,13 @@ function buildManagedMemoryBody(rawBody, memoryID, options) { const openingTag = `<${COMMENT_MEMORY_TAG} id="${memoryID}">`; const closingTag = ``; core.info(`comment_memory: building managed body for memory_id='${memoryID}'`); - let body = `${MANAGED_COMMENT_HEADER}\n\n${openingTag}\n` + `${COMMENT_MEMORY_CODE_FENCE}\n${sanitizeContent(rawBody)}\n${COMMENT_MEMORY_CODE_FENCE}\n` + `${closingTag}`; + let body = `${MANAGED_COMMENT_HEADER} + +${openingTag} +${COMMENT_MEMORY_CODE_FENCE} +${sanitizeContent(rawBody)} +${COMMENT_MEMORY_CODE_FENCE} +${closingTag}`; const tracker = getTrackerID("markdown"); if (tracker) { From 072d528ac37a1180223f904073e2cde85a55fc58 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:01:21 +0000 Subject: [PATCH 4/7] test(comment-memory): add edge-case coverage for fenced memory parsing Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c58aea4c-9179-42ff-b29b-9bbb205e5f9b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/comment_memory_helpers.cjs | 3 ++- .../setup/js/comment_memory_helpers.test.cjs | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/comment_memory_helpers.cjs b/actions/setup/js/comment_memory_helpers.cjs index 1ba45c451f4..d370c857d12 100644 --- a/actions/setup/js/comment_memory_helpers.cjs +++ b/actions/setup/js/comment_memory_helpers.cjs @@ -29,7 +29,8 @@ function stripCommentMemoryCodeFence(content) { return trimmed; } - const trailing = trimmed.slice(closingFenceStart + COMMENT_MEMORY_CODE_FENCE.length + 1).trim(); + const closingFenceEnd = closingFenceStart + 1 + COMMENT_MEMORY_CODE_FENCE.length; + const trailing = trimmed.slice(closingFenceEnd).trim(); if (trailing.length > 0) { return trimmed; } diff --git a/actions/setup/js/comment_memory_helpers.test.cjs b/actions/setup/js/comment_memory_helpers.test.cjs index 5b19b5ebcd6..b58fd249c8b 100644 --- a/actions/setup/js/comment_memory_helpers.test.cjs +++ b/actions/setup/js/comment_memory_helpers.test.cjs @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest"; -import { extractCommentMemoryEntries, isSafeMemoryId } from "./comment_memory_helpers.cjs"; +import { extractCommentMemoryEntries, isSafeMemoryId, stripCommentMemoryCodeFence } from "./comment_memory_helpers.cjs"; describe("comment_memory_helpers", () => { it("extracts managed memory entries", () => { @@ -12,6 +12,26 @@ describe("comment_memory_helpers", () => { expect(entries).toEqual([{ memoryId: "default", content: "hello" }]); }); + it("keeps fenced text unchanged when trailing content exists after closing fence", () => { + const content = "``````\nhello\n``````\ntrailing"; + expect(stripCommentMemoryCodeFence(content)).toBe(content); + }); + + it("keeps fenced text unchanged when closing fence is missing", () => { + const content = "``````\nhello"; + expect(stripCommentMemoryCodeFence(content)).toBe(content); + }); + + it("keeps malformed fenced text unchanged", () => { + const content = "``````hello\n``````"; + expect(stripCommentMemoryCodeFence(content)).toBe(content); + }); + + it("strips valid fenced text with extra newlines before content", () => { + const content = "``````\n\nhello\n``````"; + expect(stripCommentMemoryCodeFence(content)).toBe("hello"); + }); + it("rejects unsafe memory IDs", () => { const warning = vi.fn(); const entries = extractCommentMemoryEntries('\nhello\n', warning); From 73db6af188aa4f6e88589de82273a41808494c57 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:04:03 +0000 Subject: [PATCH 5/7] refactor(comment-memory): tighten fenced content parsing guards Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c58aea4c-9179-42ff-b29b-9bbb205e5f9b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/comment_memory_helpers.cjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/actions/setup/js/comment_memory_helpers.cjs b/actions/setup/js/comment_memory_helpers.cjs index d370c857d12..b524a374fef 100644 --- a/actions/setup/js/comment_memory_helpers.cjs +++ b/actions/setup/js/comment_memory_helpers.cjs @@ -14,7 +14,10 @@ const COMMENT_MEMORY_PROMPT_END_MARKER = ""; const COMMENT_MEMORY_PROMPT_END_MARKER = ""; const COMMENT_MEMORY_CODE_FENCE = "``````"; +const ESCAPED_COMMENT_MEMORY_CODE_FENCE = COMMENT_MEMORY_CODE_FENCE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); function stripCommentMemoryCodeFence(content) { const trimmed = typeof content === "string" ? content.trim() : ""; @@ -21,8 +22,7 @@ function stripCommentMemoryCodeFence(content) { if (!trimmed.startsWith(COMMENT_MEMORY_CODE_FENCE)) { return trimmed; } - const escapedFence = COMMENT_MEMORY_CODE_FENCE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const match = trimmed.match(new RegExp(`^${escapedFence}[^\\n]*\\n([\\s\\S]*)\\n${escapedFence}$`)); + const match = trimmed.match(new RegExp(`^${ESCAPED_COMMENT_MEMORY_CODE_FENCE}[^\\n]*\\n([\\s\\S]*)\\n${ESCAPED_COMMENT_MEMORY_CODE_FENCE}$`)); if (!match) { return trimmed; }