Repo header ref pill always shows the default branch (main @ sha) regardless of the ref in the URL — visiting /tree/{branch} doesn't update the pill #252

Closed
opened 2026-09-09 20:22:16 +00:00 by crueber · 3 comments
Owner

What's wrong

Visiting a tree at a non-default ref — e.g. https://hub.packden.us/crueber/walhub/tree/feat/issue-120 — renders the page content for that ref, but the repo header's ref pill still shows main @ 17d8363cb5 (the default branch head). The pill doesn't reflect the branch/sha being viewed, so nothing in the header tells you which ref you're on (screenshot: URL says feat/issue-120, pill says main @ 17d8363cb5).

Note the content IS correct — this is a header/state bug, not a resolution bug. (The sibling raw-link 404 for slashed branches is filed separately as #251.)

Root cause (code evidence)

  • The pill's label is built from the shared repo summary, not from the URL: web/src/pages/Repo.jsx:222 — label = ${shortRef(head().name)} @ ${String(head().sha).slice(0, 10)}``, where head is props.head fed from the shared summary signal (Repo.jsx:219-221, the "head pill" from issue #214). The summary is fetched once per repo and shared by every tab.
  • The summary API is ref-blind by design: GET …/api (internal/api/summary.go, §9.1) answers Summary(ctx, id) with no ref parameter (internal/api/bind_wal.go:493-504 → summarizeSnapshot(snap)); Head is the HEAD (default branch) target only. There is no wire field for "the ref currently being viewed" — and there can't be, since the summary is ref-agnostic.
  • RefPicker.pick (Repo.jsx:245-250) navigates to /{full}/tree/{ref} — the URL changes, the Tree page re-resolves and renders the new ref's content, but nothing re-renders the pill because the summary didn't change (same repo, same HEAD).
  • Deep links disagree the same way: paste a /tree/{branch} URL, land directly on the branch, pill still says main.

Expected

The header pill should show the ref the Code tab is currently viewing: branch/tag name + its head sha. When no specific ref is in the URL (bare repo view), the default-branch head is correct.

Fix direction (for implementer)

The data needed is already client-side: useResolved's step-1 resolve (web/src/lib/data.js:396-410) returns the resolved ref/sha/kind for the URL's rest — the Tree page already consumes it.

  1. Recommended: lift the resolved ref into RepoCtx (the shell context built at Repo.jsx:494-506): Tree (and Blob/Commit/Checks — every ref-addressed tab) publishes "currently viewed ref" into context; the pill reads context-first, falling back to the summary's default-branch head when the current tab has no ref (Issues/Pulls/Settings). One fetch (resolve), zero new API calls.
  2. Alternative: derive the pill purely from the URL (/tree/{ref}/… → parse) — simpler, but blob/commit pages are sha-addressed and need a ref back-map, so the context approach is more honest.
  • Preserve #214's properties: the pill still opens the ref picker on click; a background summary refresh still moves the pill when the user is viewing the default branch (the getter-based head update noted at Repo.jsx:219-220).

Acceptance criteria

  • Visiting /{o}/{r}/tree/{branch} (slashed or simple branch name) shows that branch's name + head sha in the pill; deep-linking does the same.
  • Switching branches via the picker updates the pill immediately, no full reload.
  • Tag and sha-addressed views (blob at sha, commit detail) display the corresponding ref or short sha honestly.
  • Non-ref pages (Issues, Pulls, Settings) keep showing the default-branch head — no #214 regression.
  • A background push to the viewed branch updates the pill's sha (live-refresh behavior preserved).
  • Headless unit test for the pill-label derivation: ref-in-context → name @ shortsha; no ref → default head; sha-addressed → short sha.
## What's wrong Visiting a tree at a non-default ref — e.g. `https://hub.packden.us/crueber/walhub/tree/feat/issue-120` — renders the page content for that ref, but the repo header's ref pill still shows **`main @ 17d8363cb5`** (the default branch head). The pill doesn't reflect the branch/sha being viewed, so nothing in the header tells you which ref you're on (screenshot: URL says `feat/issue-120`, pill says `main @ 17d8363cb5`). Note the content IS correct — this is a header/state bug, not a resolution bug. (The sibling raw-link 404 for slashed branches is filed separately as #251.) ## Root cause (code evidence) - The pill's label is built from the shared repo summary, not from the URL: `web/src/pages/Repo.jsx:222` — `label = `${shortRef(head().name)} @ ${String(head().sha).slice(0, 10)}``, where `head` is `props.head` fed from the shared summary signal (Repo.jsx:219-221, the "head pill" from issue #214). The summary is fetched once per repo and shared by every tab. - The summary API is ref-blind by design: `GET …/api` (`internal/api/summary.go`, §9.1) answers `Summary(ctx, id)` with no ref parameter (`internal/api/bind_wal.go:493-504` → `summarizeSnapshot(snap)`); `Head` is the HEAD (default branch) target only. There is no wire field for "the ref currently being viewed" — and there can't be, since the summary is ref-agnostic. - `RefPicker.pick` (`Repo.jsx:245-250`) navigates to `/{full}/tree/{ref}` — the URL changes, the Tree page re-resolves and renders the new ref's content, but nothing re-renders the pill because the summary didn't change (same repo, same HEAD). - Deep links disagree the same way: paste a `/tree/{branch}` URL, land directly on the branch, pill still says main. ## Expected The header pill should show the ref the Code tab is currently viewing: branch/tag name + its head sha. When no specific ref is in the URL (bare repo view), the default-branch head is correct. ## Fix direction (for implementer) The data needed is already client-side: `useResolved`'s step-1 resolve (`web/src/lib/data.js:396-410`) returns the resolved `ref`/`sha`/`kind` for the URL's rest — the Tree page already consumes it. 1. **Recommended: lift the resolved ref into RepoCtx** (the shell context built at Repo.jsx:494-506): Tree (and Blob/Commit/Checks — every ref-addressed tab) publishes "currently viewed ref" into context; the pill reads context-first, falling back to the summary's default-branch head when the current tab has no ref (Issues/Pulls/Settings). One fetch (resolve), zero new API calls. 2. Alternative: derive the pill purely from the URL (`/tree/{ref}/…` → parse) — simpler, but blob/commit pages are sha-addressed and need a ref back-map, so the context approach is more honest. - Preserve #214's properties: the pill still opens the ref picker on click; a background summary refresh still moves the pill when the user is viewing the default branch (the getter-based head update noted at Repo.jsx:219-220). ## Acceptance criteria - [ ] Visiting `/{o}/{r}/tree/{branch}` (slashed or simple branch name) shows that branch's name + head sha in the pill; deep-linking does the same. - [ ] Switching branches via the picker updates the pill immediately, no full reload. - [ ] Tag and sha-addressed views (blob at sha, commit detail) display the corresponding ref or short sha honestly. - [ ] Non-ref pages (Issues, Pulls, Settings) keep showing the default-branch head — no #214 regression. - [ ] A background push to the viewed branch updates the pill's sha (live-refresh behavior preserved). - [ ] Headless unit test for the pill-label derivation: ref-in-context → `name @ shortsha`; no ref → default head; sha-addressed → short sha.
Author
Owner

Fix PR: #261 (branch fix/issue-252). Pill is now context-first over a viewed signal published by Tree/Blob/Commits/Commit/CheckDetail, summary head as fallback; no backend change. node --test 516/516 green, vite+esbuild green. Browser proof open (shared daemon blocks loopback).

Fix PR: https://git.packden.us/crueber/walhub/pulls/261 (branch fix/issue-252). Pill is now context-first over a viewed signal published by Tree/Blob/Commits/Commit/CheckDetail, summary head as fallback; no backend change. node --test 516/516 green, vite+esbuild green. Browser proof open (shared daemon blocks loopback).
Author
Owner

Review of PR #261 (fix/issue-252, commit 2a7eeb5) — verified in scratch worktree, main untouched.

Pill derivation (web/src/lib/ref-pill.js): viewed-first via pillHead + summary fallback, all tab types covered — Tree/Blob/Commits publish resolved {name,sha}, Commit/CheckDetail publish {name:"",sha}; Issues/Pulls/Settings/Checks-list/Releases (+Labels/Milestones/Wal/Pull*/Issue*/Release* — grep confirms zero setViewed outside the 5 publishers) stay silent so fallback holds, no #214 regression. Unmount clearing present in all 5 publishers; repo-switch checked: tab unmount always clears (no persistent leak), same-component reuse (Tree->Tree across repos) self-corrects when the new resolve lands — only a transient stale-pill flash during load, same class as the pre-existing summary loading window. Non-blocking; optional follow-up is a full()-change clear in Repo.jsx (createEffect already imported, web/src/pages/Repo.jsx:7).

shortRef single definition: sole def in ref-pill.js:14, re-exported from Repo.jsx:15, Tree/Blob/Commits importers untouched — no duplicate/drift.

sha-view: pillLabel returns short sha when name empty or name===sha — never 'sha @ sha' / ' @ sha'. Placeholder 'refs' contract kept.

Zero new fetches: Tree/Blob/Commits reuse their existing useResolved payload; Commit/CheckDetail read route params only. No new useData/useResolved calls.

Live-update preserved: head stays a getter (Repo.jsx:555 pillHead(getViewed(), s().head)); resolve step-1 5s SWR (data.js:397,418) republishes on push. Commits remounts per ref via the keyed Show (Commits.jsx:199). RefPicker navigation unchanged.

No new deps: package.json untouched (solid-js+router+marked+dompurify only); ref-pill.js import-free. Laws 1/8/12 hold; law 7 n/a (no long work).

Doc entry (12_web_ui.md) accurate — 'keyed CommitList' claim checks out (Commits.jsx:199). One nit: the #247 line above it gained a double-space in this diff (whitespace only).

VERIFY: node --test web/test/unit/*.test.js => 516/516 pass (initial 7 file-failures were missing node_modules in the fresh worktree — symlinked main's for the run; PR's own viewed-ref-pill + ref-picker-pill 18/18 green regardless). vite build exit 0, esbuild exit 0 (run via node_modules/.bin directly; pnpm wrapper refuses symlinked modules dir — env quirk only). No browser check per task rules (node tests + reasoning only).

MERGE RECOMMENDATION: ready to merge.

Review of PR #261 (fix/issue-252, commit 2a7eeb5) — verified in scratch worktree, main untouched. Pill derivation (web/src/lib/ref-pill.js): viewed-first via pillHead + summary fallback, all tab types covered — Tree/Blob/Commits publish resolved {name,sha}, Commit/CheckDetail publish {name:"",sha}; Issues/Pulls/Settings/Checks-list/Releases (+Labels/Milestones/Wal/Pull*/Issue*/Release* — grep confirms zero setViewed outside the 5 publishers) stay silent so fallback holds, no #214 regression. Unmount clearing present in all 5 publishers; repo-switch checked: tab unmount always clears (no persistent leak), same-component reuse (Tree->Tree across repos) self-corrects when the new resolve lands — only a transient stale-pill flash during load, same class as the pre-existing summary loading window. Non-blocking; optional follow-up is a full()-change clear in Repo.jsx (createEffect already imported, web/src/pages/Repo.jsx:7). shortRef single definition: sole def in ref-pill.js:14, re-exported from Repo.jsx:15, Tree/Blob/Commits importers untouched — no duplicate/drift. sha-view: pillLabel returns short sha when name empty or name===sha — never 'sha @ sha' / ' @ sha'. Placeholder 'refs' contract kept. Zero new fetches: Tree/Blob/Commits reuse their existing useResolved payload; Commit/CheckDetail read route params only. No new useData/useResolved calls. Live-update preserved: head stays a getter (Repo.jsx:555 pillHead(getViewed(), s().head)); resolve step-1 5s SWR (data.js:397,418) republishes on push. Commits remounts per ref via the keyed Show (Commits.jsx:199). RefPicker navigation unchanged. No new deps: package.json untouched (solid-js+router+marked+dompurify only); ref-pill.js import-free. Laws 1/8/12 hold; law 7 n/a (no long work). Doc entry (12_web_ui.md) accurate — 'keyed CommitList' claim checks out (Commits.jsx:199). One nit: the #247 line above it gained a double-space in this diff (whitespace only). VERIFY: node --test web/test/unit/*.test.js => 516/516 pass (initial 7 file-failures were missing node_modules in the fresh worktree — symlinked main's for the run; PR's own viewed-ref-pill + ref-picker-pill 18/18 green regardless). vite build exit 0, esbuild exit 0 (run via node_modules/.bin directly; pnpm wrapper refuses symlinked modules dir — env quirk only). No browser check per task rules (node tests + reasoning only). MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #261 (review clean; viewed-first derivation, unmount clearing, single shortRef; 516/516), merged. Closing.

Fixed by PR #261 (review clean; viewed-first derivation, unmount clearing, single shortRef; 516/516), merged. Closing.
crueber added this to the v1 milestone 2026-09-10 22:27:09 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#252
No description provided.