Skip to content

Commit c16084b

Browse files
d-csclaude
andcommitted
fix(webapp): auto-redirect to root span on direct nav to buffered/PG run-detail URL
The project-scoped run-detail route (`_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx`) did not select a root span when the URL had no `?span=` query param. The sibling redirect routes `runs.$runParam.ts`, `@.runs.$runParam.ts` and `projects.v3.$projectRef.runs.$runParam.ts` all inject the span on their redirect Location, but direct navigation to the canonical project-scoped URL never hits those — leaving `selectedSpanId` undefined and the right detail panel collapsed. This affected both buffered and PG-resident runs: anyone reaching the URL via bookmark, shared link, or back-button without the query param got the empty-panel placeholder. Detect the missing param in the loader (skipping `_data` requests so client revalidations don't trigger an extra redirect), look up the `run.spanId` we already have at that point, and 302 to the same URL with the param appended. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9faf087 commit c16084b

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,24 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
294294
});
295295

296296
if (buffered) {
297+
// Preselect the root span on the initial page load when the URL
298+
// doesn't already carry `?span=`. The sibling redirect routes
299+
// (runs.$runParam.ts, @.runs.$runParam.ts,
300+
// projects.v3.$projectRef.runs.$runParam.ts) all do this, but
301+
// direct navigation to the canonical project-scoped URL never
302+
// hit those redirects — leaving the right detail panel collapsed.
303+
// Skip on `_data` requests (Remix data fetches): they're
304+
// client-driven follow-ups and the client URL is what matters,
305+
// not the loader's view of it.
306+
if (
307+
!url.searchParams.has("span") &&
308+
!url.searchParams.has("_data") &&
309+
buffered.run.spanId
310+
) {
311+
url.searchParams.set("span", buffered.run.spanId);
312+
throw redirect(url.pathname + "?" + url.searchParams.toString());
313+
}
314+
297315
const parent = await getResizableSnapshot(request, resizableSettings.parent.autosaveId);
298316
const tree = await getResizableSnapshot(request, resizableSettings.tree.autosaveId);
299317

@@ -309,6 +327,20 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
309327
throw error;
310328
}
311329

330+
// Preselect the root span on the initial page load when the URL
331+
// doesn't already carry `?span=`. See the comment on the equivalent
332+
// block in the buffered fallback above — the sibling redirect routes
333+
// do this, but direct navigation to the canonical project-scoped URL
334+
// never hits them, leaving the right detail panel collapsed.
335+
if (
336+
!url.searchParams.has("span") &&
337+
!url.searchParams.has("_data") &&
338+
result.run.spanId
339+
) {
340+
url.searchParams.set("span", result.run.spanId);
341+
throw redirect(url.pathname + "?" + url.searchParams.toString());
342+
}
343+
312344
//resizable settings
313345
const parent = await getResizableSnapshot(request, resizableSettings.parent.autosaveId);
314346
const tree = await getResizableSnapshot(request, resizableSettings.tree.autosaveId);

0 commit comments

Comments
 (0)