mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-02 16:58:52 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Task documents keep saved work and its revision history with each task. > - The browser document journey must reopen the saved task and read the revised document after reload. > - Reload can finish before the task controls mount. > - The helper checked visibility once and could wait for a tab launcher inside a panel it had never opened. > - This pull request waits for the existing usable controls before it selects the normal UI path. > - The saved content, revision, ownership and no-agent-work checks stay unchanged. ## Linked Issues or Issue Description Refs: #18356. **What happened?** The document case in QA run 41253428446 passed document creation and revision-history checks, then failed during reopen or readback. The preserved screen shows the revised document and revision 2 in chat. The original result has the generic `journey_operation_failed` code. Its exception stack was intentionally excluded, so the exact historical failing instruction is not known. A separate Chromium control reproduces a concrete navigation race in the same helper. If task controls mount 200 ms after reload, the helper can select `Open a new tab` before it opens the properties panel. That control never becomes visible. The same race affects a delayed classic document editor or an existing Artifacts tab. **Expected behavior** The helper waits for the current task's normal authoring or panel controls. It then opens Artifacts through the same visible controls. A task without document authoring must still fail. A stale body, stale title or damaged revision history must still fail. **Steps to reproduce** 1. Open a task from the Backlog list and reload it. 2. Delay mounting the task controls by 200 ms. 3. Run the unchanged document reopen helper with its properties panel closed. 4. Observe that it waits for `Open a new tab` while `Show properties` is visible. **Paperclip version or commit** Frozen source base: `b889ac39b7fbed558c5f4cfbbc2b73c4ca916810`. No duplicate document reopen PR was found. The related document feature is #18356. ## What Changed - Wait for an existing authoring, properties, Artifacts or launcher control before choosing the navigation path. - Wait for usable panel content after opening properties. - Add five credential-free Chromium controls for delayed collapsed, Artifacts, launcher and classic layouts, plus missing-authoring rejection. - Keep the existing Backlog navigation test compatible with the new readiness probes. - Register the browser regression in the existing support suite. No product code, model, budget, run cap or campaign deadline changed. The existing document contract needs no documentation change. ## Verification - Original failure is preserved. The [sanitized QA report](https://pages.paperclip.ing/evals-qa-smoke-41253428446-1/) records both partial checks and the failed journey. This PR does not change that grade. - Before the fix, the real Chromium control failed for delayed collapsed, Artifacts and classic controls. The launcher path and missing-authoring rejection passed. A 750 ms action timeout bounded this diagnostic; final tests use the existing suite timeouts. - Final Chromium: `node node_modules/@playwright/test/cli.js test --config tests/runner-e2e/playwright-support.config.ts task-document-navigation.spec.ts` — 5 passed. - Document units: `node node_modules/vitest/vitest.mjs run --config tests/runner-e2e/vitest.config.ts tests/runner-e2e/product-journeys/task-document-flow.test.ts tests/runner-e2e/product-journeys/task-document-evidence.test.ts --maxWorkers 1` — 23 passed. This includes 19 negative content, ownership and revision controls. - Scoped strict TypeScript, map checks, token gates and diff checks passed. - Node 24 normal QA import preflight passed with network disabled. - The entire `taskDocumentPersistence` function and the canonical evidence module are byte-identical to the base. The existing exact title/body checks, independent API reads, revision pointers and no-agent-work checks remain in force. - Repository-wide build, typecheck and tests were not run locally for this four-file harness change. [Canonical CI 41255897484](https://github.com/paperclipai/paperclip/actions/runs/41255897484) passed on exact head `ffc04500cb0728f6fbc8b3bc2e704c547015641d`: all 59 checks are terminal (55 success, 3 skipped, 1 neutral), with no failed or pending checks. Greptile and Paperclip each gave this head 5/5 with no findings. All review threads were checked; none are open and there is no next page. The branch is CLEAN and MERGEABLE. - Actual QA measurement now passes in [workflow 41266385118](https://github.com/paperclipai/paperclip-cloud/actions/runs/41266385118), campaign `journey-1793502283113-8f658807`. The measured harness `5c0830f693da2ccef062612146b76629a0177049` contains the three exact reviewed document source/test blobs and unchanged canonical evidence module from this head. The target was the QA cloud instance on App `756599e4ea069c9338c320fe8846523d3dfb92a0`. All four canonical checks and all five phases passed; the manifest is COMPLETE with evidence cleanup complete and nothing pending. A positive no-agent-work check establishes zero native work despite omitted raw run IDs. A separate read-only verifier reread the exact original final grade and confirmed the retained-result validator. Raw document snapshots were not retained; content, revision and author proof comes from the executed unchanged exact UI/API predicates. The full 14-case launch remains failed because hiring failed separately; no aggregate pass is claimed. ## Risks The historical exception was intentionally sanitized. The local control proves this helper race, but cannot prove which instruction failed in the prior QA attempt. The current staging measurement passes this document case; the exact historical failing instruction remains unproven. The new readiness waits use the existing assertion timeout and campaign deadline. They do not accept missing controls or weaken document evidence. ## Model Used OpenAI Codex, based on GPT-6, with reasoning, code execution and browser automation. The exact serving model revision and context-window size are 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 Live qualification: the document case is verified on QA with complete cleanup. Historical failed results remain unchanged. Co-authored-by: Paperclip <noreply@paperclip.ing>
43 lines
2.8 KiB
TypeScript
43 lines
2.8 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { reopenTaskDocument } from "./product-journeys/task-document-flow.js";
|
|
import type { JourneyContext } from "./product-journeys/types.js";
|
|
|
|
for (const layout of ["collapsed", "artifacts", "launcher", "classic"] as const) {
|
|
test(`document reopen waits for the ${layout} task controls after reload`, async ({ page }) => {
|
|
const issue = { title: "Journey QA document navigation" };
|
|
let taskLoads = 0;
|
|
await page.route("https://document-navigation.test/**", async route => {
|
|
if (new URL(route.request().url()).pathname.endsWith("/issues")) {
|
|
await route.fulfill({ contentType: "text/html", body: `<a href="/QA/issues/QA-201">${issue.title}</a>` });
|
|
return;
|
|
}
|
|
taskLoads++;
|
|
await route.fulfill({ contentType: "text/html", body: `<main><div id="controls"></div></main><script>
|
|
const controls = document.querySelector('#controls');
|
|
function authoring() { controls.innerHTML = '<button>New document</button><section id="document-qa-note">Saved revision two</section>'; }
|
|
function panel() {
|
|
controls.innerHTML = ${JSON.stringify(layout === "artifacts" ? '<button role="tab">Artifacts</button>' : '<button>Open a new tab</button>')};
|
|
controls.querySelector('button').onclick = ${layout === "artifacts" ? "authoring" : `() => { controls.innerHTML = '<div role="option">Artifacts</div>'; controls.querySelector('[role="option"]').onclick = authoring; }`};
|
|
}
|
|
setTimeout(() => {
|
|
${layout === "classic" ? "authoring();" : layout === "collapsed" ? `controls.innerHTML = '<button>Show properties</button>'; controls.querySelector('button').onclick = () => setTimeout(panel, 100);` : "panel();"}
|
|
}, 200);
|
|
</script>` });
|
|
});
|
|
const context = { page, api: { origin: "https://document-navigation.test" }, company: { issuePrefix: "QA" } } as JourneyContext;
|
|
await reopenTaskDocument(context, issue);
|
|
await expect(page.locator("#document-qa-note")).toHaveText("Saved revision two");
|
|
expect(taskLoads).toBe(2);
|
|
});
|
|
}
|
|
|
|
test("document reopen fails when the task has no authoring controls", async ({ page }) => {
|
|
await page.route("https://document-navigation.test/**", async route => {
|
|
const list = new URL(route.request().url()).pathname.endsWith("/issues");
|
|
await route.fulfill({ contentType: "text/html", body: list ? '<a href="/QA/issues/QA-201">Journey QA document navigation</a>' : '<main>Task without document permissions</main>' });
|
|
});
|
|
const context = { page, api: { origin: "https://document-navigation.test" }, company: { issuePrefix: "QA" } } as JourneyContext;
|
|
await expect(reopenTaskDocument(context, { title: "Journey QA document navigation" })).rejects.toThrow();
|
|
await expect(page.getByRole("button", { name: "New document", exact: true })).toHaveCount(0);
|
|
});
|