From 53923e5adb77450970b76240f462783bd663ec27 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 3 Oct 2026 03:02:50 -0500 Subject: [PATCH] Preserve explicit publication references for internal files Co-authored-by: Paperclip --- doc/plans/2026-10-02-pi-production-readiness.md | 6 ++++++ .../native-deliverable-feedback.test.ts | 5 +++++ .../native-runtime/native-deliverable-feedback.ts | 14 ++++++++++---- .../native-runner-file-handoff.test.ts | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/doc/plans/2026-10-02-pi-production-readiness.md b/doc/plans/2026-10-02-pi-production-readiness.md index f6911d1d6af..693bf647e0c 100644 --- a/doc/plans/2026-10-02-pi-production-readiness.md +++ b/doc/plans/2026-10-02-pi-production-readiness.md @@ -421,6 +421,12 @@ prices are estimates. Unpriced usage must stay unpriced in the ledger and UI. including both publication bypasses and both internal/denied variants. A free replay of the exact retained server-bound objective changes from a false download requirement to the intended internal outcome, with no grader or deadline changes. +- At `b28422b29`, fresh review finds that an explicit "attach it" can lose its + publication requirement when the preceding file is called internal. The free + unit/database regressions reproduce that bypass. The correction preserves + attachment/export references and resolves other publication pronouns against + the preceding file output. Inline replies remain inline. All 74 completion + tests pass; the exact failed agent-files objective still needs no download. - 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 3d2d2b35a91..3fcdc3703ed 100644 --- a/server/src/services/native-runtime/native-deliverable-feedback.test.ts +++ b/server/src/services/native-runtime/native-deliverable-feedback.test.ts @@ -19,6 +19,9 @@ describe("explicit file output requirements", () => { "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.", "Attempt native write to /outside/probe.json and write report.pdf; this negative test must be denied.", + "Write report.txt and attach it. This is an internal verification file, not a deliverable.", + "Attach it.", + "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); }); @@ -44,6 +47,8 @@ describe("explicit file output requirements", () => { "Before finishing, attempt native write exactly once to /outside/pi-unassigned.txt with content forbidden. This intentionally unassigned root must be denied.", "Write internal-proof.txt; then check it exists. This is an internal verification file, not a deliverable.", "Attempt native write to /outside/probe.txt and create no files. This negative test must be denied.", + "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.", ])("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 375bdd2284c..43c25d41040 100644 --- a/server/src/services/native-runtime/native-deliverable-feedback.ts +++ b/server/src/services/native-runtime/native-deliverable-feedback.ts @@ -87,13 +87,18 @@ export function explicitlyRequestsFileOutput(objective: string): boolean { if (/^files?$/iu.test(match[0]) && /^\s+(?:tools?|permissions?|systems?|formats?|names?|paths?|types?|sizes?|descriptors?)\b/iu.test(suffix)) return false; return true; }); - const downloadable = !/\b(?:no|without)\s+(?:downloadable|attached)/iu.test(output) - && /\b(?:downloadable|attached)\s+(?:file|report|document|checklist|draft)\b/iu.test(output); + // An explicit attachment/export request can refer to the file by + // pronoun. It still requires publication when its name is omitted. + const referencedOutput = /^(?:attach|export|send|provide|give|return)$/iu.test(create[0]) + && /^\s+(?:me\s+)?(?:it|them|this|that)\b/iu.test(output); + const referencedAttachment = referencedOutput && /^(?:attach|export)$/iu.test(create[0]); + 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 }]; + return [{ objects: objects.length, downloadable, publication, deniedAttempt, referencedOutput }]; }); }); // "This ..." qualifies a single requested file in the preceding sentence, @@ -101,7 +106,8 @@ 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) + return outputs.some((output, outputIndex) => (output.objects > 0 || output.downloadable + || (output.referencedOutput && (outputs[outputIndex - 1]?.objects ?? 0) > 0)) && (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 7d490096a52..69f0575ec56 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 @@ -352,6 +352,7 @@ describe("native runner file handoff", () => { it.each([ "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.", ])("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));