diff --git a/apps/desktop/e2e/code-review-overview.spec.ts b/apps/desktop/e2e/code-review-overview.spec.ts index ddf91079..4b32dcf4 100644 --- a/apps/desktop/e2e/code-review-overview.spec.ts +++ b/apps/desktop/e2e/code-review-overview.spec.ts @@ -18,11 +18,13 @@ async function open(hostile = false, mode = "normal") { const ready = (value: unknown) => ({ state: "ready", value, fetchedAt: NOW }); const failed = { state: "unavailable", reason: "offline", fetchedAt: NOW }; const overview = structuredClone(req.key?.number === 91 ? R4.overview : R1.overview); + if (g.__overviewMode === "renamed") overview.locator = { ...overview.locator, owner: "current-org", repo: "lending-renamed" }; if (hostile) overview.body = '# Hostile fixture\n\n\n\n[bad](javascript:alert(1))\n\n![remote](https://invalid.example/tracker.svg)\n\n[Safe](https://example.com/path)'; let result: unknown = failed; if (req.op === "connection") result = R1.connection; if (req.op === "inbox") result = R1.inbox; if (req.op === "overview") result = ready(overview); + if (req.op === "overview" && g.__overviewMode === "overview-unavailable") result = failed; if (req.op === "checks") result = g.__overviewMode === "stale" && R1.checks.state !== "unavailable" ? { ...R1.checks, state: "stale", reason: "offline" } : R1.checks; if (req.op === "stack") result = R1.stack; if (req.op === "activity" && R2.activity.state !== "unavailable") { @@ -46,6 +48,25 @@ async function open(hostile = false, mode = "normal") { await page.locator('[data-testid="code-review-row"][data-pr="example-org/lending#73"]').first().click(); } test.afterEach(async () => { await app?.close(); }); +test("the loaded repository identity updates header and links and survives an unavailable refresh", async () => { + await open(false, "renamed"); + const id = page.getByTestId("code-review-pr-id"); + await expect(id).toHaveText("lending-renamed #73"); + await expect(id).toHaveAttribute("title", "current-org/lending-renamed #73"); + const url = "https://github.com/current-org/lending-renamed/pull/73"; + await expect(page.getByTestId("code-review-open-github")).toHaveAttribute("href", url); + await app.evaluate(({ ipcMain }) => { + ipcMain.removeHandler("clipboard:write"); + ipcMain.handle("clipboard:write", (_event, request) => { (globalThis as any).__copiedPrLink = request.text; }); + }); + await page.getByTestId("code-review-copy-link").click(); + await expect.poll(() => app.evaluate(() => (globalThis as any).__copiedPrLink)).toBe(url); + await app.evaluate(() => { (globalThis as any).__overviewMode = "overview-unavailable"; }); + await refresh(); + await expect(page.getByTestId("code-review-overview-error")).toBeVisible(); + await expect(id).toHaveText("lending-renamed #73"); + await expect(page.getByTestId("code-review-open-github")).toHaveAttribute("href", url); +}); test("R1 metadata and R2 pagination stay truthful, deduplicated and keyboard accessible", async () => { await open(); await expect(page.getByRole("heading", { name: R1.overview.title, exact: true })).toBeVisible(); diff --git a/apps/desktop/src/renderer/code-review/CodeReviewView.tsx b/apps/desktop/src/renderer/code-review/CodeReviewView.tsx index 1c2607fa..0e49e7c3 100644 --- a/apps/desktop/src/renderer/code-review/CodeReviewView.tsx +++ b/apps/desktop/src/renderer/code-review/CodeReviewView.tsx @@ -114,7 +114,7 @@ export default function CodeReviewView({ active, inboxOpen, layout, setLayout, o if (outcome.status === "mismatch") return void readConnectionRef.current(); const result = outcome.result; if (result.state !== "unavailable" && !sameKey(result.value.key, pr.key)) return; - setPrs((p) => p[id] ? { ...p, [id]: { ...p[id]!, overview: result } } : p); + setPrs((p) => p[id] ? { ...p, [id]: { ...p[id]!, locator: result.state === "unavailable" ? p[id]!.locator : result.value.locator, overview: result } } : p); if (result.state !== "unavailable") void readContext(pr, result.value.revision); // A key for another account than gh's: the account changed under us. if (result.state === "unavailable" && result.reason === "signed-out") void readConnectionRef.current(); diff --git a/docs/reviews/2026-11-01-cr4-current-locator.md b/docs/reviews/2026-11-01-cr4-current-locator.md new file mode 100644 index 00000000..635a1869 --- /dev/null +++ b/docs/reviews/2026-11-01-cr4-current-locator.md @@ -0,0 +1,15 @@ +# Current PR repository identity + +The PR header and links retained the locator from the inbox or resolved link after an overview read returned a newer repository name. Main already supplies the current locator with the stable repository-id-bound overview; the renderer stored that overview without adopting its locator. + +The renderer now updates the open PR's locator after its existing account and PR-key checks accept the overview. An unavailable refresh preserves the last accepted locator. This also keeps consumers of the open PR, including header links, on the same accepted identity. No read or write endpoint, permission, or provider behavior changes. + +## Regression evidence + +The Electron regression supplies an inbox row for `example-org/lending#73` and an overview for the same stable PR key at `current-org/lending-renamed#73`. Before the fix, it failed because the header remained `lending #73`. After the fix, it passed the header text and title, GitHub destination, copied destination, and preservation after an unavailable refresh (1 passed, 4.9 seconds). The fixture replaces reads and clipboard delivery; it performs no live GitHub write or provider call. + +Independent review found no blockers and verified the existing account and stable-key guards, unavailable-refresh retention, and protection against older inbox locators. The unchanged full unit retry passed with 2,277 passes and 26 skips; full E2E also passed (479 desktop tests, 7 skips; 7 site tests). This repairs the C22 data-binding gap identified in the CR-8 refresh; original-reference comparison, human accessibility and live acceptance remain separate. + +The first full validation attempt passed build, then reported a failure in the unchanged `remove completes the real CLI archive flow` test after 23.7 seconds. The worktree suite finished with 119 passes and one failure: the archive subprocess returned a null exit status at the success assertion, and the chained run stopped before desktop tests or E2E. At observation, machine load averaged about 178 and swap use was about 76 GB; the fixture has a 20-second subprocess timeout. Resource pressure is a hypothesis, not a proven cause. The original log is preserved. One unchanged, isolated retry of that exact test passed in 5.3 seconds; this diagnostic retry does not establish full validation. + +The canonical `pnpm test` retry completed successfully: worktree 120 passed, core 16 passed, desktop 2,094 passed and 20 skipped, browser bridge 21 passed and 6 skipped, store 10 passed, and store desktop 16 passed. The chained `pnpm test:e2e` completed with exit 0: 479 desktop tests passed and 7 were skipped in 32.7 minutes, followed by 7 site tests passed in 9.1 seconds. The locator regression passed within that full run in 6.5 seconds. The five-launch transcript benchmark median was 113.4 ms against the unchanged 500 ms limit. The first full-run failure remains part of the record; this retry used the same source and test limits. diff --git a/docs/status.md b/docs/status.md index 2289ae9c..898fce93 100644 --- a/docs/status.md +++ b/docs/status.md @@ -233,6 +233,8 @@ The published application source is `6a97d23aaea315b29008890f25dde2d7ea07f1d0` ( ## In flight +- **Current Code Review repository identity.** A reproduced C22 gap kept the inbox's repository name in the PR header and links after the overview returned a new name. The candidate adopts the overview locator after the existing PR-key check and retains it across an unavailable refresh. The Electron regression failed before the fix and passed afterward; independent review found no blockers, and the full unit retry passed (2,277 passes, 26 skips). Full E2E passed (479 desktop tests, 7 skips; 7 site tests). [Evidence](reviews/2026-11-01-cr4-current-locator.md). + - **Deferred Markdown for long threads (hosted cold-open budget).** Hosted CI's approved 500 ms cold-open gate failed on runner variance alone: 33 hosted runs on 2026-10-30 had a 377.7 ms median and a 781.9 ms failure on application code identical to a 436.1 ms pass. Val chose on 2026-10-30 to defer offscreen answers. Turns before the newest eight show their Markdown as written until it nears the view, then parse once; scroll anchoring keeps the reader's place. Locally the five-launch median fell from about 190 to 88 ms, and 3× CPU throttling cut it from 806–870 to 312–340 ms. The gate is unchanged. The first hosted run had a 129.9 ms median (481 desktop E2E passes). It landed through #463 (`32f1cb4`) on 2026-10-31 UTC and is unreleased. [Evidence](reviews/2026-10-30-deferred-markdown.md). Desktop CI memory diagnostics landed through #458 after #456’s cold transcript budget failure. The workflow reuses the existing resource sampler and retains its log as a separate CI artifact; it does not establish the cause of the earlier failure or change the 500 ms gate. The #458 queue passed desktop and site E2E and retained its memory log. See [the diagnostic review](reviews/2026-10-30-desktop-ci-memory.md).