Skip to content

OpenConceptLab/ocl_issues#2929 | Repo panels ask only the version the URL names after Back, Forward or a version change - #95

Merged
snyaggarwal merged 4 commits into
mainfrom
OpenConceptLab/ocl_issues#2929-panel-version-gate
Nov 5, 2026
Merged

snyaggarwal merged 4 commits into
mainfrom
OpenConceptLab/ocl_issues#2929-panel-version-gate

Conversation

@paynejd

@paynejd paynejd commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

What changed and why

On a repository page, after Back, Forward or a version change, the concept and mapping panels could ask the version the page had just left. RepoHome keeps the previous route's repo in state until the new one loads, and the panels build their request URLs from it.

The ticket asks whether that's one wasted request or wrong data. It's wrong data. A panel loads once per URL, so when the right repo arrives it doesn't ask again. On main, after the ticket's steps, the panel keeps showing the version just left while the URL and the version chip name another. When the resource isn't in the version just left, the panel says "this mapping could not be found" about a mapping that exists.

Three changes, one commit each, then a fourth with the cuts from the simplification review:

  1. The gate (panelReady.js, RepoHome.jsx). Panels open only when isSameRepoScope(repoRoute, route), the check the concept list got in OpenConceptLab/ocl_issues#2910 | Repository default view; the Hierarchy view ignores filters #68. A version change now always closes the panel for a render, so it remounts with the right repo.
  2. Late responses (RepoHome.jsx). The gate alone leaves two races, both already on main. Press Back while a picked version is still loading, and its late response replaced the repo in state: a blank list and panel under the wrong version chip. On a collection, a late expansions response first pointed the panel at the wrong version, then blanked the page. Responses for a version the page has left are now dropped.
  3. A closed concept panel (ConceptHome.jsx). A panel closed with a request still out carried on when the answer came, and asked $cascade of the version just left. That's the request the ticket describes. It now stops.

Two things beyond the letter of the ticket:

  • The reference panel gets the same gate. The acceptance criterion names the concept and mapping panels, but this is the same flaw with a one-condition fix. Reproduced on main: with Expansion Results open, reload, then go Back to a version that has to load, and its list asks the version just left.
  • RESERVED_ROUTE_KEYWORDS moves from constants.js into repoRoute.js, its only user. constants.js imports package.json, which kept repoRoute.js from loading under node:test.

Behavior to check in review:

  • A panel URL with a version, opened directly, now waits for the repo before asking for the resource, as URLs without a version and the list already did. That also removes a duplicate repo request (2 to 1).
  • A panel's own tab (Details/History, Metadata/Expansion Results) resets when Back or Forward crosses versions, because the panel remounts. Moving within one version keeps it.

How I tested

The pr.yml gate, every step as written, on Node 22, from a clean export of the commit: secret-variable check passes, npm ci passes, ./node_modules/eslint/bin/eslint.js src --ext .jsx,.js passes, npm test passes 122 of 122. The eslint step ran from an export because inside a worktree nested in a checkout, ESLint also loads the outer checkout's config. In the worktree itself, --no-eslintrc -c .eslintrc.json also passes. PR checks pass on the same commit.

Unit tests: src/components/repos/__tests__/panelReady.test.js, 8 tests on real route parsing. Removing the scope check from the helper fails 4 of them. They cover the decision only: this repo can't render components in a test, so the browser check covers RepoHome and the panels.

Browser check: headless Chromium, fresh context, 1440x900, npm start against the staging API, anonymous and read-only, on a public source and a public collection with released versions. The script records every concept and mapping request with the version it targets, and stamps each detail response with that version, so the panel's header says whose data it shows. Slow-response scenarios delay one request in the browser. The same script ran on the base commit and on this branch's last commit, and I looked at the screenshots from both. 14 of 18 scenarios fail on main, with 30 requests to the wrong version. On this branch none fail, and no request goes to the wrong version.

Scenario main (d6c42b3) This branch
Concept panel, no version in the URL: pick a release, Back (the ticket's steps) Asks the release, shows its data Asks HEAD, shows HEAD
HEAD named in the URL: pick a release, Back Asks the release, shows its data Asks HEAD, shows HEAD
Concept panel on a version: pick another, Back, Forward, open a row, Back, Forward Asks the version just left each time, shows its data Asks the URL's version, shows it
Same, with a reload before Back, so the version isn't cached Asks the version just left, shows its data Asks the URL's version, shows it
Mapping panel on a version: pick another, Back Asks the version just left; "could not be found" when the mapping isn't in it Asks the URL's version, shows the mapping
Mapping panel, no version in the URL: pick a release, Back Asks the release, shows its data Asks HEAD, shows HEAD
Collection concept panel: pick another, open a row, Back, Forward Asks the version just left, then corrects itself Asks only the URL's version
Reference panel on Expansion Results: reload, Back, slow repo Its list asks the version just left Waits for the repo; no request to the version left
Back while the concept request is out Two $cascade to the version just left None
Pick a version while the concept request is out Two $cascade to the version just left None
Back while the first $cascade is out A second $cascade to the version just left None
Back while the picked version is loading List blank, chip names the wrong version List, chip and panel all right
Collection: Back while the picked version's expansions load A request to the wrong version, then list and panel blank List, chip and panel all right
Rapid Back and Forward across versions, slow repo Panel ends on the wrong version Panel ends on the URL's version
First load of a panel URL with a version Right; 2 repo requests Right; 1 repo request
One version: open a row, switch row, reload, close Right Right
Unknown version in a panel URL Redirects; 1 doomed panel request Redirects; no panel request
Unknown repo in a panel URL Not-found page and panel message Same

I also sampled every animation frame during a Back and a Forward between two cached versions with a panel open. No frame loses the split layout: the panel shows its skeleton, then the right version.

No UI strings or components changed, so there's no locale or Design System work.

Review

Codex pass 1 and its final check on the cuts were both clean. The simplification review listed 9 items: 6 applied, 2 applied in part and 1 kept, each answered in the comments below. Nothing is open.

Acceptance criteria

  • After Back/Forward or a version change, the concept and mapping panels request only from the version the URL asks for. Evidence: the browser check above records no concept or mapping request to any other version on this branch, and panelReady.test.js pins the gate.

Docs

None changed. No doc describes this behavior (README, AGENTS.md). New comments say why the gate and the guards exist.

Chose not to do

  • No component or browser test in the repo. It has no jsdom or Playwright, and adding either goes beyond this ticket. The browser script stayed out of the repo for the same reason.
  • No closed-panel guard on the mapping panel. Its late responses start no request against a repo version.

Noticed, not fixed

Both checked in the browser on this branch. This PR doesn't touch either path.

  • Within one version, a slow answer can replace a newer one. Click a concept row whose answer is slow, then another row: the URL names the second row and the panel ends up showing the first. It doesn't cross versions, so it's outside this ticket.
  • A late latest/ response overrides a pick. On a bare repo URL, pick a version while the latest one is still resolving: the page moves to your pick, then jumps to the latest version.

closes OpenConceptLab/ocl_issues#2929

🤖 Generated with Claude Code

https://claude.ai/code/session_01AaZ4qTkzts4TQWehztSkyg

Written by an Octoad session worker (worker-v0.17, recipe lean-loop, flow public>public, run openconceptlab-ocl-issues-2929-20261104-114900).

paynejd and others added 3 commits November 4, 2026 12:30
… version the route asks for

After Back, Forward or a version change, RepoHome keeps the previous route's
repo in state until the new one loads. The concept and mapping panels built
their requests from it and load once per URL, so they asked the version the
page had left and kept showing its answer, or a 404 when the resource isn't
in that version.

Gate the panels on the check the concept list already uses
(#68), in a pure helper with tests. The reference panel
gets the same gate: its Expansion Results tab asked the wrong version too.

RESERVED_ROUTE_KEYWORDS moves into repoRoute.js, its only user, so the helper's
tests can run real route parsing under node:test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AaZ4qTkzts4TQWehztSkyg
…es for a version the page has left

Press Back while the picked version is still loading, and its late response
replaced the repo and route marker in state. The list and panel then waited
for a route that was gone: a blank page under the wrong version chip.

A late expansions response did the same on a collection, after pointing the
panel at the version the page had left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AaZ4qTkzts4TQWehztSkyg
…asking its version for more

A concept panel that closed with a request still out carried on when the
answer came: it asked $cascade of the version the page had just left, and the
first $cascade's answer started a second one. The panel now closes on every
version change, so these were the last requests against the wrong version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AaZ4qTkzts4TQWehztSkyg
@paynejd paynejd added origin/ai-generated Issue or PR created by an AI agent origin/session-worker Opened by the Octoad session worker; a run record backs it labels Nov 4, 2026

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex review, pass 1 (codex-cli 0.166.0, commit 37c4406)

Clean.

findings.json
{
  "reviewer": "codex-cli 0.166.0",
  "perspective": "defects",
  "mode": "full",
  "commit": "37c440666e54847768ba798b7facb70fde50a4ca",
  "verdict": "clean",
  "prior": [],
  "findings": []
}

…review

Drop test lines that repeat others, a renaming wrapper and defaults that change
nothing. Guard only the follow-up request in getMappings, and shorten the
helper's comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AaZ4qTkzts4TQWehztSkyg

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification review (commit 37c4406)

9 items.

  • S1 · dup-test · src/components/repos/tests/panelReady.test.js:25 · lines 25-26 repeat line 24: the helper never reads the tab → nothing replaces them (-2)
  • S2 · dup-test · src/components/repos/tests/panelReady.test.js:31 · lines 31-32 are weaker forms of lines 45-47 → nothing replaces them; line 30 and lines 46-47 cover the ready path (-2)
  • S3 · dup-test · src/components/repos/tests/panelReady.test.js:45 · line 45 repeats line 46, which also varies the resource → nothing replaces it (-1)
  • S4 · dup-test · src/components/repos/tests/panelReady.test.js:41 · line 41 mirrors line 40, though it alone catches a one-way change → nothing replaces it; keep it to pin both directions (-1)
  • S5 · yagni · src/components/repos/tests/panelReady.test.js:9 · a wrapper that only renames parseRepoPath → call parseRepoPath directly (-1)
  • S6 · shrink · src/components/repos/tests/panelReady.test.js:13 · three falsy defaults in the test helper change nothing → leave them out (-3)
  • S7 · shrink · src/components/concepts/ConceptHome.jsx:186 · the early return in getMappings only needs to stop the follow-up → guard just the getInverseMappings call (-2)
  • S8 · delete · src/components/concepts/ConceptHome.jsx:67 · setting aliveRef true in the effect guards a case that needs StrictMode or Fast Refresh → drop it; if it stays, one-line the cleanup (-5)
  • S9 · shrink · src/components/repos/panelReady.js:3 · the header comment restates the function name and repeats itself → a two-line comment (-1)
findings.json
{"reviewer":"claude subagent","perspective":"simplify","mode":"full","commit":"37c440666e54847768ba798b7facb70fde50a4ca","verdict":"findings","prior":[],"findings":[{"id":"S1","severity":null,"tag":"dup-test","file":"src/components/repos/__tests__/panelReady.test.js","line":25,"claim":"Cut lines 25-26. They repeat line 24: isPanelReady never receives the tab and isSameRepoScope compares only repo identity and version, so the mappings and references variants take the same branch and do not show that those panels wait. In a mutation check against 13 variants of isPanelReady, every variant these two lines catch is also caught by lines 20, 24, 36 or 40.","scenario":null,"action":"Nothing replaces them; line 24 stays.","lines_delta":-2,"confidence":"high"},{"id":"S2","severity":null,"tag":"dup-test","file":"src/components/repos/__tests__/panelReady.test.js","line":31,"claim":"Cut lines 31-32. Each compares a versioned route with an identical versioned repo route, which is a weaker form of lines 45-47, where the resource, tab or expansion also differs. Line 32 changes only the tab and version strings, and the tab is never read. Line 30 stays as the no-version case. The mutation check found nothing only these lines catch.","scenario":null,"action":"Nothing replaces them; line 30 and lines 46-47 cover the ready path.","lines_delta":-2,"confidence":"high"},{"id":"S3","severity":null,"tag":"dup-test","file":"src/components/repos/__tests__/panelReady.test.js","line":45,"claim":"Cut line 45. Line 46 already varies the resource (456 against none) and the tab, so a scope check that wrongly compared resources fails on line 46 as well. Line 45's only extra is that it names the resource case alone.","scenario":null,"action":"Nothing replaces it; line 46 stays and the test title still holds.","lines_delta":-1,"confidence":"medium"},{"id":"S4","severity":null,"tag":"dup-test","file":"src/components/repos/__tests__/panelReady.test.js","line":41,"claim":"Line 41 mirrors line 40 through the same symmetric version ===, so it could go. It is not a pure duplicate: it is the only line that catches a one-way change where a no-version route accepts a HEAD repo. Nothing in the repo does that today; line 40 still catches the two-way version of that change. Keep it if the HEAD and no-version pair is meant to be pinned in both directions.","scenario":null,"action":"Nothing replaces it; line 40 stays, and the test title loses 'each'.","lines_delta":-1,"confidence":"low"},{"id":"S5","severity":null,"tag":"yagni","file":"src/components/repos/__tests__/panelReady.test.js","line":9,"claim":"const route = path => parseRepoPath(path) is a renaming wrapper used only inside isReady.","scenario":null,"action":"Call parseRepoPath directly on lines 11-12.","lines_delta":-1,"confidence":"high"},{"id":"S6","severity":null,"tag":"shrink","file":"src/components/repos/__tests__/panelReady.test.js","line":13,"claim":"The three falsy defaults in isReady change nothing: isPanelReady treats undefined the same as false for all three. All 16 assertions gave their expected results with the defaults removed, run by a scratch script that re-encodes them against the real module.","scenario":null,"action":"const isReady = (path, repoPath, overrides = {}) => isPanelReady({route: parseRepoPath(path), repoRoute: repoPath ? parseRepoPath(repoPath) : null, ...overrides}), keeping the existing line breaks.","lines_delta":-3,"confidence":"medium"},{"id":"S7","severity":null,"tag":"shrink","file":"src/components/concepts/ConceptHome.jsx","line":186,"claim":"The early return in getMappings' .then only needs to stop the follow-up request, as the comment on line 64 says. The other work in that handler is state setters, which do nothing after unmount on React 18, plus a pure filter. Trade-off: this differs from the early-return form on line 81, which has to stay because it stops several follow-ups.","scenario":null,"action":"Remove lines 186-187 and change the last line of the handler to: !directOnly && aliveRef.current && getInverseMappings(concept, includeRetired)","lines_delta":-2,"confidence":"medium"},{"id":"S8","severity":null,"tag":"delete","file":"src/components/concepts/ConceptHome.jsx","line":67,"claim":"aliveRef.current = true guards a case that can't happen here. useRef(true) already starts true, and the cleanup runs only at unmount. A re-run of the effect with the ref preserved would need StrictMode or React Fast Refresh, and grep finds neither in src, public, package.json, .babelrc or webpack.config.js. Trade-off: this diverges from RepoHome's copy of the pattern (line 344), and whoever turns on StrictMode later would have to restore the line, so if it stays, one-lining the cleanup still saves 2 lines.","scenario":null,"action":"Nothing replaces the line; lines 66-71 become React.useEffect(() => () => { aliveRef.current = false }, []). If the line stays: return () => { aliveRef.current = false }, the form used in src/hooks/useMapProjectCount.js.","lines_delta":-5,"confidence":"medium"},{"id":"S9","severity":null,"tag":"shrink","file":"src/components/repos/panelReady.js","line":3,"claim":"The first sentence of the header comment restates the function name, and the last two sentences each describe the repo the previous route left in state.","scenario":null,"action":"// A panel builds its requests from the repo it's given and loads once per URL. Back and Forward leave the\n// previous route's repo in state until the route's own has loaded, and a panel opened on it asks that version.","lines_delta":-1,"confidence":"medium"}]}

@paynejd

paynejd commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Response to the simplification review

  • S1 applied in 0306e56
  • S2 applied in part in 0306e56: line 32 cut. Line 31 kept: it's the positive half of the v1 against v2 pair this fix is about.
  • S3 applied in 0306e56
  • S4 kept: it pins the pair in both directions, it alone catches a one-way change, and both directions occur in browser history.
  • S5 applied in 0306e56
  • S6 applied in 0306e56
  • S7 applied in 0306e56
  • S8 applied in part in 0306e56: cleanup one-lined. aliveRef.current = true kept: RepoHome's copy of the pattern has it, and without it a future StrictMode would silently stop every panel load.
  • S9 applied in 0306e56, keeping "keep the answer", which says why the wrong request is more than a wasted one.

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex check of commits 37c4406..0306e56

Clean. (codex-cli 0.166.0)

findings.json
{"reviewer":"codex-cli 0.166.0","perspective":"defects","mode":"final","commit":"0306e56cf0adefa084939e633ee1e5567ea01645","verdict":"clean","prior":[],"findings":[]}

@snyaggarwal
snyaggarwal merged commit e686264 into main Nov 5, 2026
2 checks passed
@snyaggarwal
snyaggarwal deleted the OpenConceptLab/ocl_issues#2929-panel-version-gate branch November 5, 2026 02:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

origin/ai-generated Issue or PR created by an AI agent origin/session-worker Opened by the Octoad session worker; a run record backs it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TBv3: concept panel fetches from the previous version after Back/Forward

2 participants