Honor negated attachment requests in task completion (#17399)

This commit is contained in:
Nicky Leach authored and GitHub committed 2026-10-23 17:38:50 -07:00
1 parent 72a5fecb58
commit a978ecadef
3 files changed
+24 -4

No files matched your search

@@ -6,6 +6,9 @@ describe("explicit file output requirements", () => {
"Prepare a requested file",
"Make a Markdown file named checklist.md with three items.",
"Export the results as a CSV.",
"Do not modify repository files, but create an attachment.",
"Do not create attachments; export the results as CSV.",
"Do not create attachments and instead export a CSV.",
"Give me a downloadable report.",
"Please create out/answer.pdf and attach it.",
"Do not use external services. Create a file with the results.",
@@ -37,6 +40,13 @@ describe("explicit file output requirements", () => {
"Read the file and explain what it does.",
"Review the PDF and answer in the chat.",
"Do not create a file; answer inline.",
"Run hostname, id, and pwd on your assigned computer. Report their output in a short completion comment, followed by CONTROL_PLANE_CHECK_OK. Do not modify repository files or create attachments. Mark the task done when finished.",
"Do not modify repository files or create attachments.",
"Do not edit files and do not create attachments.",
"Do not create files; instead, don't create attachments either.",
"Do not create files and attach them.",
"Don’t edit files or create attachments.",
"Never edit files nor create attachments.",
"Don't attach a file. Reply with three bullets.",
"Fix a crash in parser.ts.",
"Read the file and write a short explanation inline.",
@@ -87,7 +87,12 @@ export function explicitlyRequestsFileOutput(objective: string): boolean {
const creates = [...clause.matchAll(/\b(?:create|make|write|save|export|attach|send|generate|produce|prepare|provide|give|return|build)\b/giu)];
return creates.flatMap((create, createIndex) => {
const before = clause.slice(0, create.index);
if (/\b(?:do not|don't|never|no need to)\s*$/iu.test(before)) return [];
if (/\b(?:do not|don['’]t|never|no need to)\s*$/iu.test(before)) return [];
const negation = /\b(?:do not|don['’]t|never|no need to)\b/iu.exec(before);
if (negation) {
const rest = before.slice(negation.index + negation[0].length);
if (!/\binstead\b/iu.test(rest) && /^(?:\s*|[\s\S]*\b(?:and|or|nor)\s*)$/iu.test(rest)) return [];
}
// Bind each object to its own verb. A denied write or "create no files"
// cannot suppress a separate requested report in this same clause.
const output = clause.slice(create.index + create[0].length, creates[createIndex + 1]?.index);
@@ -325,9 +325,14 @@ describe("native runner file handoff", () => {
await db.update(issueWorkProducts).set({ status: "failed" }).where(eq(issueWorkProducts.id, product.id));
await expect(nativeCompletionFeedback(db, runId, doneReport([`work_product:${product.id}`])))
.rejects.toThrow("requested file has no accessible delivery evidence");
await db.update(heartbeatRuns).set({ contextSnapshot: { issueId, executionContinuation: { objective: "Do not create a file. Explain the result inline." } } }).where(eq(heartbeatRuns.id, runId));
await expect(nativeCompletionFeedback(db, runId, doneReport([])))
.resolves.toContain("Completion report accepted");
for (const objective of [
"Do not create a file. Explain the result inline.",
"Run hostname, id, and pwd. Report the output in a completion comment. Do not modify repository files or create attachments.",
]) {
await db.update(heartbeatRuns).set({ contextSnapshot: { issueId, executionContinuation: { objective } } }).where(eq(heartbeatRuns.id, runId));
await expect(nativeCompletionFeedback(db, runId, doneReport([])))
.resolves.toContain("Completion report accepted");
}
} finally {
await db.delete(issueWorkProducts).where(eq(issueWorkProducts.id, product.id));
await db.update(heartbeatRuns).set({ contextSnapshot: { issueId } }).where(eq(heartbeatRuns.id, runId));