From 0c48f5e72773877e35c84b8fccee9d8e9de1995e Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:33:46 -0600 Subject: [PATCH] test(runner): cover document guidance for file-only objectives (#18669) ## Thinking Path > - Completion guidance can link a saved task document even when the user requested only a file. > - The validator loads document publication proof when the objective requests a document or the completion report cites one. > - The success tests added in #18661 all requested both a document and a CSV. > - Those tests therefore could not detect loss of the cited-document branch. > - This test requests only a CSV and resubmits the actual guidance link with a registered file receipt. ## Linked Issues or Issue Description Addresses the [coverage warning on #18661](https://github.com/paperclipai/paperclip/pull/18661#discussion_r4447457009). **What happened?** The earlier success cases exercised the document-requested condition. The independently cited-document condition lacked regression coverage. **Expected behavior:** A file-only objective can cite a verified saved-document guidance link alongside the required registered file. **Steps to reproduce:** Publish a task document, register a CSV, request only the CSV, obtain the saved-document href from completion guidance, and submit both pieces of evidence. Removing only the cited-document lookup must make this case fail. **Paperclip version or commit:** Current master after #18661. **Deployment mode:** Local source tests with real PostgreSQL and native publication receipts; the remote CSV reader is controlled. No deployment is part of this follow-up. ## What Changed - Allow the existing registered-file test helper to accept a file-only objective. - Add one success case using real document publication, file registration, and the server-generated guidance href. - Keep production source and the existing negative cases unchanged. ## Verification - All 27 native completion feedback tests pass on current production code. - The new case fails when only `|| citesTaskDocument` is temporarily removed from publication lookup. The production file is restored exactly afterward. This demonstrates a coverage gap; it does not establish a new production defect. - Server typecheck and `git diff --check` pass. - Full repository CI passed, including both required E2E and verify aggregate gates. Greptile scored the current head 5/5 with no unresolved findings. The separate optional Paperclip review is queued and has supplied no finding. The preceding production PR records its separate local full-suite timeout and isolated passing retry; this follow-up uses the focused local suite and full CI rather than repeating that long local run. ## Risks Low risk: only the existing test file and its fixture helper change. The new case depends on real document/file publication contracts, including the CSV receipt requirement. ## Model Used OpenAI `gpt-6.1-sol` through Codex, with reasoning, code editing, shell execution, and automated tests. The context-window size is not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip --- .../native-completion-feedback.test.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server/src/services/native-runtime/native-completion-feedback.test.ts b/server/src/services/native-runtime/native-completion-feedback.test.ts index ddd445cf6f6..ad9ba109e86 100644 --- a/server/src/services/native-runtime/native-completion-feedback.test.ts +++ b/server/src/services/native-runtime/native-completion-feedback.test.ts @@ -32,8 +32,8 @@ describe("native final-response feedback", () => { } }) as { document: { id: string; latestRevisionId: string }; documentHref: string }; return { companyId, agentId, issueId, runId, saved }; } - async function publishedFileReport(value: Awaited>): Promise { - await db.update(issues).set({ description: "Save a document on this task. Export the results as a CSV." }).where(eq(issues.id, value.issueId)); + async function publishedFileReport(value: Awaited>, objective = "Save a document on this task. Export the results as a CSV."): Promise { + await db.update(issues).set({ description: objective }).where(eq(issues.id, value.issueId)); const bytes = Buffer.from("category,total\nverified,3\n"); const authority = new PaperclipRunnerToolAuthority(db, { companyId: value.companyId, agentId: value.agentId, issueId: value.issueId, runId: value.runId, workspaceRoot: "/workspace/test", executionTargetKind: "remote", readRemoteTaskFile: async () => bytes }); @@ -234,6 +234,17 @@ describe("native final-response feedback", () => { await expect(nativeCompletionFeedback(db, value.runId, referenced)) .resolves.toContain("Completion report accepted"); }); + it("accepts its generated document guidance link when the objective requests only a CSV", async () => { + const value = await fixture(); + await db.update(issues).set({ title: "Export the results as a CSV" }).where(eq(issues.id, value.issueId)); + const report = await publishedFileReport(value, "Export the results as a CSV."); + const feedback = await nativeCompletionFeedback(db, value.runId, report); + const href = /\[Saved document\]\(([^)]+)\)/u.exec(feedback)?.[1]; + expect(href).toBe(value.saved.documentHref); + await expect(nativeCompletionFeedback(db, value.runId, { + ...report, evidence: [...report.evidence, { ref: href! }], + })).resolves.toContain("Completion report accepted"); + }); it("retains foreign task, foreign company, bogus link, and local file guards", async () => { const current = await fixture(), otherTask = await fixture(current.companyId), otherCompany = await fixture(); const report = await publishedFileReport(current);