diff --git a/tests/runner-e2e/PRODUCT-JOURNEYS.md b/tests/runner-e2e/PRODUCT-JOURNEYS.md index 66983e18052..672f93331a2 100644 --- a/tests/runner-e2e/PRODUCT-JOURNEYS.md +++ b/tests/runner-e2e/PRODUCT-JOURNEYS.md @@ -54,6 +54,9 @@ it does not retry the journey. The model still computes the result, publishes the file, saves the document, and completes the tasks. Independent download, math, provenance, document, and parent-link checks remain authoritative. Historical failed attempts retain their original fixture and outcome. +The parent's final reply must use Markdown links to the saved child results, +including the attachment's real content or download path. Naming a file or +document key in inline code does not satisfy the existing parent-link check. Save this credential-free local target config outside the repository: diff --git a/tests/runner-e2e/product-journeys/onboarding-first-task.test.ts b/tests/runner-e2e/product-journeys/onboarding-first-task.test.ts index 3908de12cb3..02f688b347f 100644 --- a/tests/runner-e2e/product-journeys/onboarding-first-task.test.ts +++ b/tests/runner-e2e/product-journeys/onboarding-first-task.test.ts @@ -237,7 +237,7 @@ describe("Cloud canonical onboarding first task", () => { } }); await expect(onboardingFirstTask(f.input)).rejects.toMatchObject({ code: mode === "no-child" ? "onboarding_completed_without_child" : "onboarding_approved_child_mismatch" }); }); - it.each(["parent-run", "wrong-connection", "bad-hash", "missing-document", "missing-parent-links"])("does not pass an unverified deliverable: %s", async mode => { + it.each(["parent-run", "wrong-connection", "bad-hash", "missing-document", "missing-parent-links", "filename-only-parent-result"])("does not pass an unverified deliverable: %s", async mode => { const f = fixture({ mutate(phase, state) { if (phase !== "accepted") return; if (mode === "parent-run") state.attachments[0]!.originatingRunId = "acceptance-run"; @@ -245,8 +245,9 @@ describe("Cloud canonical onboarding first task", () => { if (mode === "bad-hash") state.attachments[0]!.sha256 = "0".repeat(64); if (mode === "missing-document") state.documents = []; if (mode === "missing-parent-links") state.comments.pop(); + if (mode === "filename-only-parent-result") state.comments.at(-1)!.body = "Done: `QA-2` completed. Saved document: `connection-proof`. Deliverable published: `connection-proof.json`."; } }); - await expect(onboardingFirstTask(f.input)).rejects.toMatchObject({ code: mode === "bad-hash" ? "artifact_independent_check_failed" : mode === "missing-document" ? "onboarding_child_document_missing" : mode === "missing-parent-links" ? "onboarding_parent_result_links_missing" : "artifact_run_attribution_mismatch" }); + await expect(onboardingFirstTask(f.input)).rejects.toMatchObject({ code: mode === "bad-hash" ? "artifact_independent_check_failed" : mode === "missing-document" ? "onboarding_child_document_missing" : ["missing-parent-links", "filename-only-parent-result"].includes(mode) ? "onboarding_parent_result_links_missing" : "artifact_run_attribution_mismatch" }); expect(f.events).not.toContain("visible:download"); }); it("counts proposal and parent runs toward the campaign cap", async () => { diff --git a/tests/runner-e2e/product-journeys/onboarding-first-task.ts b/tests/runner-e2e/product-journeys/onboarding-first-task.ts index 0b394720daf..6633a6a36e1 100644 --- a/tests/runner-e2e/product-journeys/onboarding-first-task.ts +++ b/tests/runner-e2e/product-journeys/onboarding-first-task.ts @@ -188,7 +188,8 @@ export async function onboardingFirstTask(input: OnboardingFirstTaskInput): Prom ].join("\n"); await page.getByTestId("question-other-answer-composer").last().locator('[contenteditable="true"],textarea').first().fill([ `I have one task in mind. Propose exactly one child task named "${title}" and wait for my approval before creating it or doing its work.`, - "After approval, assign it to yourself and let its own Paperclip execution produce the result. Then verify and link its saved document and downloadable file in this onboarding conversation and complete both tasks.", + "After approval, assign it to yourself and let its own Paperclip execution produce the result. After it finishes, verify its saved document and published attachment.", + "In your final reply in this onboarding conversation, include actual Markdown links to the child task, its saved document, and connection-proof.json. Copy the published attachment's returned contentPath or downloadPath into the file link URL, including its actual attachment ID. A backticked filename, document key, or path is not a clickable link. Obtain the real references from the child's saved results; do not invent IDs or URLs. Then complete both tasks.", "When you create the child, copy the entire brief between BEGIN CHILD BRIEF and END CHILD BRIEF VERBATIM into create_task.description. Preserve every step, tool name, parameter instruction, and exact encoded input. Do not summarize or paraphrase it.", "BEGIN CHILD BRIEF", childBrief,