Repository navigation
OpenConceptLab/ocl_issues#2910 | Repository default view; the Hierarchy view ignores filters - #68
Conversation
…ew; the Hierarchy view ignores filters - Repository settings get a Display section with a "Default view" select (Table, Card, or Hierarchy when the source has a hierarchy root), stored as meta.display.default_view. Saved default filters are shown read-only there. - The Concepts tab opens in the saved view when it is available, else Table. A link that carries filters or a search opens in Table. - The Hierarchy view sends no facet filters: they would push the root request onto the search path, which ignores onlyHierarchyRoot and shows a flat list. Filters stay in the URL, so Table and Card keep them; the filter panel is hidden in Hierarchy, as sort already is. - Opening in Hierarchy leaves the saved default filters for the first switch to Table or Card. meta.display.default_filter keeps working as the Table and Card defaults, with no data migration. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
…description (removed in the next commit) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
…ged tree has none Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
…o version, encode q in search URLs, consume pending defaults on the first list switch Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
Codex review, pass 1 (codex-cli 0.163.0, commit 917138a)Verdict: findings (2 × P1, 1 × P2)
findings.json{
"reviewer": "codex-cli 0.163.0",
"perspective": "defects",
"mode": "full",
"commit": "917138a3581c5f79de739ad2908d3a4566ae36d9",
"verdict": "findings",
"prior": [],
"findings": [
{
"id": "F1.1",
"severity": "P1",
"tag": null,
"file": "src/components/search/Search.jsx",
"line": 90,
"claim": "Version navigation retains the previous version's default view.",
"scenario": "Switch from a released version defaulting to Table to HEAD defaulting to Hierarchy, without URL filters or a search term. RepoHome mounts Search under the new URL while still passing the previous version's metadata. The useState initializer captures Table permanently; after HEAD loads, its concepts remain a flat list instead of opening in Hierarchy.",
"action": "Initialize display and hierarchy after metadata for the requested version is available, or gate Search mounting until that version's metadata has loaded.",
"lines_delta": null,
"confidence": "high"
},
{
"id": "F1.2",
"severity": "P1",
"tag": null,
"file": "src/components/search/Search.jsx",
"line": 141,
"claim": "Applying deferred defaults corrupts search terms containing URL-special characters.",
"scenario": "Open a source in its default Hierarchy view with saved list filters, search for 'CD4+ T cells', then switch to Table. This new URL rewrite calls getCurrentLayoutURL, which interpolates q without encoding it. The URL synchronization reads the plus as a space and searches for a different term. A query such as 'blood & urine' is truncated to 'blood '.",
"action": "Preserve the encoded query using URLSearchParams when adding deferred filters, or encode q in getCurrentLayoutURL.",
"lines_delta": null,
"confidence": "high"
},
{
"id": "F1.3",
"severity": "P2",
"tag": null,
"file": "src/components/search/Search.jsx",
"line": 139,
"claim": "Deferred defaults remain pending when explicit filters override the first list visit.",
"scenario": "Open in Hierarchy, add a Class filter through the header, and switch to Table. The explicit filter correctly wins, but pendingRepoDefaultFilters stays true. Clear the filters, then switch to Hierarchy and back to Table: the saved defaults unexpectedly return, undoing the user's cleared filter state.",
"action": "Consume the pending-default flag on the first transition to Table/Card regardless of whether existing URL filters suppress applying the defaults.",
"lines_delta": null,
"confidence": "high"
}
]
} |
Response to Codex pass 1
|
…he route's version has loaded Replaces the remount key from the previous commit: Back/Forward don't set loading before Search mounts, so the gate checks the loaded version instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
Codex review, pass 2 (codex-cli 0.163.0, commit e3cfbb9)Verdict: findings (1 × P1). Prior: F1.1 unresolved, F1.2 resolved, F1.3 resolved.
findings.json{
"reviewer": "codex-cli 0.163.0",
"perspective": "defects",
"mode": "verify",
"commit": "e3cfbb90e4ad592ffb6ecb1b4ba1356898d18617",
"verdict": "findings",
"prior": [
{
"id": "F1.1",
"status": "unresolved"
},
{
"id": "F1.2",
"status": "resolved"
},
{
"id": "F1.3",
"status": "resolved"
}
],
"findings": [
{
"id": "F2.1",
"severity": "P1",
"tag": null,
"file": "src/components/repos/RepoHome.jsx",
"line": 588,
"claim": "Browser Back still applies the previous version's defaults before the corrected remount.",
"scenario": "Open HEAD with default Hierarchy and no URL filters, switch to a release with default Table and saved list filters, then press Back. Unlike the version dropdown, Back does not synchronously set loading. Search mounts with the release's metadata and loading=false, inserting its filters into HEAD's URL before RepoHome's loading effect runs. Once HEAD loads, the new Search sees those filters and opens in Table. Reproduced using the actual components in an in-memory hook harness.",
"action": "Gate Search initialization on the loaded repository version matching the requested route. Prevent stale metadata from applying defaults before that match, rather than relying only on the key and effect-driven loading state.",
"lines_delta": null,
"confidence": "high"
}
]
} |
Response to Codex pass 2
|
…the loaded repo was fetched for Replaces the version comparison, which never matched /latest/ links and let a versionless route accept the previous version's repo after Back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
Codex review, pass 3 (codex-cli 0.163.0, commit 1035d75)Verdict: findings (2 × P1). Prior: F1.1, F1.2, F1.3 resolved; F2.1 regressed by its fix.
findings.json{
"reviewer": "codex-cli 0.163.0",
"perspective": "defects",
"mode": "verify",
"commit": "1035d75f18845c357b35d8954dd3623e6da56e3c",
"verdict": "findings",
"prior": [
{
"id": "F1.1",
"status": "resolved"
},
{
"id": "F1.2",
"status": "resolved"
},
{
"id": "F1.3",
"status": "resolved"
},
{
"id": "F2.1",
"status": "regressed"
}
],
"findings": [
{
"id": "F3.1",
"severity": "P1",
"tag": null,
"file": "src/components/repos/RepoHome.jsx",
"line": 542,
"claim": "The version gate prevents valid /latest/ links from rendering Search.",
"scenario": "Open /orgs/o/sources/s/latest/concepts/ when the latest released version is v1. The API returns version='v1', which never equals route.version='latest', so Search remains unmounted. Reproduced with the actual component in an in-memory harness; the previous reviewed commit renders and fetches concepts.",
"action": "Track the requested route scope associated with the loaded response, or resolve the latest alias before comparing versions.",
"lines_delta": null,
"confidence": "high"
},
{
"id": "F3.2",
"severity": "P1",
"tag": null,
"file": "src/components/repos/RepoHome.jsx",
"line": 542,
"claim": "Back navigation to a versionless route still applies the previous version's defaults.",
"scenario": "Open /orgs/o/sources/s/concepts/root/ with HEAD defaulting to Hierarchy, select v1 with Table and saved filters, then press Back. The !route.version branch accepts the still-loaded v1 metadata before the loading effect runs. Search writes v1's filters into the versionless URL, causing HEAD to open in Table with those filters. Reproduced in the component harness.",
"action": "Require the loaded repository's request scope to match the current route even when route.version is empty, before mounting Search.",
"lines_delta": null,
"confidence": "high"
}
]
} |
Response to Codex pass 3
Checked in a local build, against a source with HEAD saved as Hierarchy and a released version saved as Table with list defaults:
That was the third and last defect pass. A Codex check of the commits after 1035d75 follows the simplification review. Noticed, not changed here: after Back to a versionless concept-panel URL, the concept detail panel still requests |
…es over the fetch's route Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
Simplification review (codex-cli 0.163.0, commit 21dc683)The recipe's fresh Claude subagent wasn't available this session, so a fresh read-only Codex run did this review.
Answers:
simplify.json{
"reviewer": "codex-cli 0.163.0",
"perspective": "simplify",
"mode": "full",
"commit": "21dc683000e4a6cd7bcd64c402e12a7e63d79068",
"verdict": "findings",
"prior": [],
"findings": [
{
"id": "S1",
"severity": null,
"tag": "native",
"file": "src/components/repos/RepoCreateDisplay.jsx",
"line": 33,
"claim": "The installed MUI TextField with select already composes FormControl, InputLabel, Select, and FormHelperText.",
"scenario": null,
"action": "Replace the four wrappers and their imports with TextField select, retaining the existing MenuItems, disabled condition, value, onChange, label, and helperText. Set variant='outlined' and size='medium' explicitly, since the theme defaults TextField to small; preserve the transparent background with sx.",
"lines_delta": -5,
"confidence": "high"
},
{
"id": "S2",
"severity": null,
"tag": "shrink",
"file": "src/components/repos/RepoHome.jsx",
"line": 154,
"claim": "The fetchedRoute parameter duplicates the route already captured by applyRepoData and fetchRepo from the same render, including delayed responses.",
"scenario": null,
"action": "Use setRepoState({repo: _repo, repoRoute: route}); remove the fetchedRoute parameter, both corresponding arguments, and const fetchedRoute = route. Keep the atomic repository/route state and isSameRepoScope render gate that fix the P1 navigation defects.",
"lines_delta": -1,
"confidence": "high"
}
]
} |
…nly the same repo and keeps the current route marker Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf
Codex check of commits 1035d75..07089da (codex-cli 0.163.0)Verdict: findings (1 × P2)
Response: FC.1 fixed in 5576348. The save callback now updates the repository functionally, only while it's still the same repository, and leaves the current route marker as it is. findings.json{
"reviewer": "codex-cli 0.163.0",
"perspective": "defects",
"mode": "cuts",
"commit": "07089dae2b4f96254672347f130a85189ad408c0",
"verdict": "findings",
"prior": [],
"findings": [
{
"id": "FC.1",
"severity": "P2",
"tag": null,
"file": "src/components/repos/RepoHome.jsx",
"line": 513,
"claim": "A late default-filter save can overwrite the current route marker and permanently hide Search.",
"scenario": "For a source without releases, open its versionless concepts URL, select HEAD, save default filters, then navigate Back before the PATCH completes. Back loads HEAD with repoRoute.version='', but the save callback restores its captured repoRoute.version='HEAD'. The new scope gate hides Search, and no fetch runs to restore it. Reproduced against both commits: Search remains visible before these cuts and disappears afterward.",
"action": "Use a functional state update that ignores save completions from a scope that is no longer current, preserving the current repository and route marker.",
"lines_delta": null,
"confidence": "high"
}
]
} |
Codex check of commits 07089da..5576348 (codex-cli 0.163.0)Verdict: clean. No P0 or P1 is open, so the review loop is done. findings.json{
"reviewer": "codex-cli 0.163.0",
"perspective": "defects",
"mode": "cuts",
"commit": "5576348af4aed17f8c67c8994b236c23ce498005",
"verdict": "clean",
"prior": [
{
"id": "FC.1",
"status": "resolved"
}
],
"findings": []
} |
Closes OpenConceptLab/ocl_issues#2910
What was wrong
A source's saved default filters (
meta.display.default_filter) applied in every view. In the Hierarchy view, any facet filter sends the root request (onlyHierarchyRoot=true) down the API's search path, which ignoresonlyHierarchyRoot. So instead of the tree, the view showed a flat filtered list with no expanders. Child rows always load from…/children/without filters, so a filter can never narrow a tree; it can only replace it. LOINC shows it today: the root request returns 1 row (ROOT), and with its savedconcept_class=LOINCdefault it returns 103,291 flat rows.What this changes
meta.display.default_view. The select reads and writes the form's Meta JSON, so the raw Meta field and the select can't disagree. Saved default filters are shown read-only in the same section.meta.display.default_filterkeeps working as the Table and Card defaults, with no new key and no data migration. When a repository opens in Hierarchy, its defaults are applied the first time the user switches to Table or Card. Until then they don't show as a filter chip over an unfiltered tree.This differs from the ticket's proposed storage: there's no
default_filterskeyed by view. A Hierarchy default could only ever replace the tree with a flat list, so only the list defaults exist. The API needed no change.metais stored and returned as sent, so every write merges into the existing object, as "Save as Default Filters" already does.Files:
Search.jsx,RepoCreate.jsx, newRepoCreateDisplay.jsx(the same Select pattern as the ID auto-assignment section), and strings in en, es, pt-BR and zh.From review:
RepoHome.jsxnow records the route each repository was fetched for and renders Search only once that matches the current route. Search reads the saved defaults when it mounts, and Back/Forward or a version switch could otherwise mount it with the previous version's repository. That race already affecteddefault_filter.Search.jsxalso URL-encodes the search term when it builds search URLs.Screenshots
Before. LOINC in production, Hierarchy view: a flat list of 103,291 concepts.

Before. A test source with a hierarchy root and a saved

conceptClass=Diagnosisdefault, Hierarchy view: 4 flat rows.After. LOINC on this branch (production data, signed out): Hierarchy shows the tree from

ROOT.After. The test source, switched from Table to Hierarchy: the tree shows and the filter panel is hidden. Switching back to Table brings back the Diagnosis filter.

After. The new Display section in the repository's edit form.

After. With the default view saved as Hierarchy, the source opens on its root, with no filter chip.

After. Switching to Card applies the saved default filters.

Testing
./node_modules/eslint/bin/eslint.js --ext .jsx,.js src/) passes, and so doesnpm test(20/20, unchanged).conceptClass=Diagnosis.meta.displaywith bothdefault_viewand the existingdefault_filter.?filters=…opens in Table.Not in this PR
eshas nosearch.hierarchystring, so "Hierarchy" falls back to English there. That predates this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01CmUEYaYnVtrDoKSnaaJdmf