diff --git a/doc/plans/2026-10-02-pi-production-readiness.md b/doc/plans/2026-10-02-pi-production-readiness.md index 980ef256c9..9532599f10 100644 --- a/doc/plans/2026-10-02-pi-production-readiness.md +++ b/doc/plans/2026-10-02-pi-production-readiness.md @@ -431,6 +431,13 @@ prices are estimates. Unpriced usage must stay unpriced in the ledger and UI. in chat do not. All 76 completion tests pass; the exact failed agent-files objective still needs no download. Wait for clean source review before rebuilding public packages again. +- At `fef9e8456`, review identifies a missing explicit "send it" delivery + requirement. Preserve delivery references to the preceding file, including + across sentences, while explicit inline/chat content remains inline. + Attachment/export directives always retain publication requirements. All 84 + completion tests pass, including both delivery and inline instructions. The + exact failed agent-files objective still needs no download; no paid retry runs + on these intermediate candidates. - The `0d65753fe` broad local suite is terminal with 736 passed files, four skipped files, 14,982 passed tests and three failures: a Git scan load single-flight count (497 versus 498) and two HTTP socket resets. One associated diff --git a/server/src/services/native-runtime/native-deliverable-feedback.test.ts b/server/src/services/native-runtime/native-deliverable-feedback.test.ts index 9d5f7f3099..c84b199b8e 100644 --- a/server/src/services/native-runtime/native-deliverable-feedback.test.ts +++ b/server/src/services/native-runtime/native-deliverable-feedback.test.ts @@ -22,6 +22,11 @@ describe("explicit file output requirements", () => { "Write report.txt and attach it. This is an internal verification file, not a deliverable.", "Attach it.", "Write report.txt and return it as an attachment. This is an internal verification file, not a deliverable.", + "Write report.txt and send it to me. This is an internal verification file, not a deliverable.", + "Write report.txt. This is an internal verification file, not a deliverable. Send it to me.", + "Write report.txt and provide it to me. This is an internal verification file, not a deliverable.", + "Write report.txt and give me it. This is an internal verification file, not a deliverable.", + "Write report.txt and return it. This is an internal verification file, not a deliverable.", ])("recognizes an explicit output request: %s", objective => { expect(explicitlyRequestsFileOutput(objective)).toBe(true); }); @@ -50,6 +55,8 @@ describe("explicit file output requirements", () => { "Write internal-proof.txt; do not attach it. This is an internal verification file, not a deliverable.", "Write a reply and return it in chat.", "Write internal-proof.txt and return it in chat. This is an internal verification file, not a deliverable.", + "Write internal-proof.txt and send it in chat. This is an internal verification file, not a deliverable.", + "Write internal-proof.txt. This is an internal verification file, not a deliverable. Return it inline.", ])("does not require a file for a text or source-review request: %s", objective => { expect(explicitlyRequestsFileOutput(objective)).toBe(false); }); diff --git a/server/src/services/native-runtime/native-deliverable-feedback.ts b/server/src/services/native-runtime/native-deliverable-feedback.ts index 9da45681a0..b1f0126741 100644 --- a/server/src/services/native-runtime/native-deliverable-feedback.ts +++ b/server/src/services/native-runtime/native-deliverable-feedback.ts @@ -64,6 +64,7 @@ async function hasCurrentPublicationReceipt(db: Db, companyId: string, receipts: */ export function explicitlyRequestsFileOutput(objective: string): boolean { const sentences = objective.replaceAll("\\_", "_").split(/(?:[.!?](?:\s|$)|\n)/iu); + let precedingFileOutput = false; return sentences.some((sentence, index) => { const file = /\b(?:files?|attachments?|downloads?|pdf|spreadsheets?|workbooks?|slide decks?|powerpoints?|docx|xlsx|csv)\b|\b[^\s/]+\.(?:md|txt|pdf|docx?|xlsx?|csv|pptx?|png|jpe?g|svg|zip)\b/giu; const outputs = sentence.split(/(?:[;,]|\bbut\b)/iu).flatMap(clause => { @@ -89,15 +90,19 @@ export function explicitlyRequestsFileOutput(objective: string): boolean { }); // An explicit attachment/export request can refer to the file by // pronoun. It still requires publication when its name is omitted. - const referencedAttachment = /^(?:attach|export)$/iu.test(create[0]) + const referencesOutput = /^(?:attach|export|send|provide|give|return)$/iu.test(create[0]) && /^\s+(?:me\s+)?(?:it|them|this|that)\b/iu.test(output); + const referencedAttachment = referencesOutput && /^(?:attach|export)$/iu.test(create[0]); + const inline = /\b(?:in (?:the )?chat|inline|as (?:a |the )?(?:chat )?(?:reply|message)|(?:its|the) contents)\b/iu.test(output); const downloadable = referencedAttachment || (!/\b(?:no|without)\s+(?:downloadable|attached)/iu.test(output) && /\b(?:downloadable|attached)\s+(?:file|report|document|checklist|draft)\b/iu.test(output)); const publication = /\b(?:downloadable|attached|attach|export|send|provide|return|give)\b/iu.test(create[0] + output); const deniedAttempt = create[0].toLowerCase() === "write" && objects.length === 1 && /\b(?:attempt|try)\s+(?:the\s+)?native\s*$/iu.test(before) && /\b(?:must be denied|denial is (?:the )?expected|(?:this|the) negative test)\b/iu.test(objective); - return [{ objects: objects.length, downloadable, publication, deniedAttempt }]; + if (objects.length === 0 && !downloadable && /^\s+(?:no|zero|without)\b/iu.test(output)) return []; + return [{ objects: objects.length, downloadable, publication, deniedAttempt, + referencesOutput, publicationReference: referencesOutput && !inline }]; }); }); // "This ..." qualifies a single requested file in the preceding sentence, @@ -105,8 +110,12 @@ export function explicitlyRequestsFileOutput(objective: string): boolean { // exception and separate report requests still require publication. const internal = outputs.reduce((count, output) => count + output.objects + Number(output.downloadable && output.objects === 0), 0) === 1 && /^\s*This is (?:an? )?(?:personal memory|internal (?:assertion|verification) file)\b[^.!?]*\bnot a (?:task )?deliverable\b/iu.test(sentences[index + 1] ?? ""); - return outputs.some(output => (output.objects > 0 || output.downloadable) - && (output.publication || (!internal && !output.deniedAttempt))); + return outputs.some(output => { + const publicationReference = output.publicationReference && precedingFileOutput; + if (!output.referencesOutput) precedingFileOutput = output.objects > 0; + return (output.objects > 0 || output.downloadable || publicationReference) + && (output.publication || (!internal && !output.deniedAttempt)); + }); }); } diff --git a/server/src/services/native-runtime/native-runner-file-handoff.test.ts b/server/src/services/native-runtime/native-runner-file-handoff.test.ts index bb9dd536b8..c6ad60b626 100644 --- a/server/src/services/native-runtime/native-runner-file-handoff.test.ts +++ b/server/src/services/native-runtime/native-runner-file-handoff.test.ts @@ -354,6 +354,7 @@ describe("native runner file handoff", () => { "Write report.pdf; write internal-proof.txt. This is an internal verification file, not a deliverable.", "Write report.pdf and attempt native write to /outside/probe.txt; this negative test must be denied.", "Write report.txt and attach it. This is an internal verification file, not a deliverable.", + "Write report.txt and send it to me. This is an internal verification file, not a deliverable.", ])("still requires publication when the task also asks for internal or denied writes: %s", async objective => { await db.update(heartbeatRuns).set({ contextSnapshot: { issueId, executionContinuation: { objective } } }) .where(eq(heartbeatRuns.id, runId));