Drop buffered inline comment when it is posted live (#1405) (#1412)

When classify_inline_comments is enabled, create_inline_comment buffers calls
without confirmed=true. The model frequently re-issues the call with confirmed=true
after reading the buffered reply, which posts the comment live but leaves the
original buffered entry behind. The post-session replay step then posts it again,
so every inline comment lands twice.

Reconcile the buffer on a live post: after a confirmed comment is created, remove
any buffered entry matching the same path, line, startLine and body so it cannot be
replayed. Extracts the reconciliation into src/mcp/inline-comment-buffer.ts (the MCP
server module starts a server on import) and adds unit tests.

Co-authored-by: archievi <13202986+archievi@users.noreply.github.com>
This commit is contained in:
Archie
2026-07-03 22:37:36 -07:00
committed by GitHub
co-authored by archievi
parent 558b1d6cab
commit a221ad2dd9
3 changed files with 204 additions and 0 deletions
+11
View File
@@ -5,6 +5,7 @@ import { appendFileSync } from "fs";
import { z } from "zod";
import { createOctokit } from "../github/api/client";
import { sanitizeContent } from "../github/utils/sanitizer";
import { removeBufferedComment } from "./inline-comment-buffer";
// Get repository and PR information from environment variables
const REPO_OWNER = process.env.REPO_OWNER;
@@ -180,6 +181,16 @@ server.tool(
const result = await octokit.rest.pulls.createReviewComment(params);
// The comment is now live. Drop any buffered copy of it so the
// post-session replay step cannot post it a second time (the model often
// re-issues a buffered call with confirmed=true after the buffer reply).
if (CLASSIFY_ENABLED) {
removeBufferedComment(
{ path, line, startLine, body: sanitizedBody },
BUFFER_PATH,
);
}
return {
content: [
{
+54
View File
@@ -0,0 +1,54 @@
import { existsSync, readFileSync, writeFileSync } from "fs";
export type BufferedCommentMatch = {
path: string;
line?: number;
startLine?: number;
body: string;
};
/**
* Remove any buffered inline comment that matches an already-posted comment.
*
* When a comment is posted live (confirmed=true), an earlier buffered copy of
* the same comment must be dropped so the post-session replay step does not
* post it a second time. The model frequently re-issues a buffered call with
* confirmed=true after reading the "Set confirmed=true to post immediately"
* reply; previously the original buffered entry was left behind and replayed,
* producing duplicate inline comments.
*
* Entries are matched on path, line, startLine and body. Lines that cannot be
* parsed are kept untouched.
*/
export function removeBufferedComment(
match: BufferedCommentMatch,
bufferPath: string,
): void {
if (!existsSync(bufferPath)) {
return;
}
const remaining = readFileSync(bufferPath, "utf8")
.split("\n")
.filter((line) => line.trim() !== "")
.filter((line) => {
let entry: BufferedCommentMatch;
try {
entry = JSON.parse(line);
} catch {
// Keep anything we cannot parse rather than silently dropping it.
return true;
}
const isSameComment =
entry.path === match.path &&
entry.line === match.line &&
entry.startLine === match.startLine &&
entry.body === match.body;
return !isSameComment;
});
writeFileSync(
bufferPath,
remaining.length > 0 ? remaining.join("\n") + "\n" : "",
);
}