You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Slice audited: navigation backbone — server/lib/navManifest.js, server/routes/palette.js, client/src/components/Layout.jsx and the section tab headers. Audit date: 2026-09-06.
Problem
client/src/components/Layout.jsx:134 imports NAV_COMMANDS statically from server/lib/navManifest.js (since #5053, 2026-08-25, when the sidebar started deriving its rows from the manifest). The same component still fetches the manifest over HTTP on every mount:
Layout.jsx:771-779 — manifestEntryByPath is built from the fetched copy.
Layout.jsx:876-889 — resolveNavEntry calls migrateLegacyNavPath(path, manifestNav) with the fetched copy.
Layout.jsx:163 — the api import exists only for this call.
server/routes/palette.js:64-66 builds the nav half of the response from the very same NAV_COMMANDS object the client bundle already contains.
Impact
One redundant request per page load, for a payload the bundle already carries.
Until the fetch resolves, resolveNavEntry returns null for the 63 manifest entries that have no sidebar presentation (/wiki/log, /goals/tree, …) and for every stored path that must go through previousPaths migration. Pinned/Recent rows for those paths render nothing on first paint and pop in later, and useNavWorkingSet's normalize effect (client/src/hooks/useNavWorkingSet.js:96-107) re-runs when the fetch lands.
If the fetch fails (network, expired auth on a password-gated install), those rows stay missing for the whole session with only a console warning.
client/src/components/Layout.test.jsx:71-78 has to mock getPaletteManifest with a hand-written one-entry manifest, so the tests exercise a fake manifest rather than the real one.
Fix
Use the static import for everything:
Delete the fetch and the manifestNav state. Build manifestEntryByPath from NAV_COMMANDS (a module-level map; keep the feature filter inside the existing useMemo on isFeatureEnabled).
Pass NAV_COMMANDS to migrateLegacyNavPath in resolveNavEntry.
Remove the api import from Layout.jsx and the getPaletteManifest mock from Layout.test.jsx; keep the eidoverse previousPaths assertion, now against the real manifest.
Leave client/src/components/CmdKSearch.jsx:133 and client/src/components/voice/VoiceWidget.jsx:175 alone — they need the actions half, which only the server can hydrate from the voice tool registry — and leave the endpoint itself as-is (documented for companion apps in docs/COMPANION_APP_API.md:138).
Acceptance criteria
Layout.jsx contains no getPaletteManifest call and no manifestNav state; git grep getPaletteManifest client/src/components/Layout.jsx is empty.
A stored pinned path with no sidebar presentation (e.g. /wiki/log) renders its Pinned row on the first render with no network mock — add this case to Layout.test.jsx.
A stored path listed in some command's previousPaths (the existing eidoverse case) still resolves to the current path on first render without the getPaletteManifest mock.
cd client && npm test -- Layout passes; server/lib/navManifest.test.js and server/routes/palette.test.js are unchanged and pass.
Slice audited: navigation backbone —
server/lib/navManifest.js,server/routes/palette.js,client/src/components/Layout.jsxand the section tab headers. Audit date: 2026-09-06.Problem
client/src/components/Layout.jsx:134importsNAV_COMMANDSstatically fromserver/lib/navManifest.js(since #5053, 2026-08-25, when the sidebar started deriving its rows from the manifest). The same component still fetches the manifest over HTTP on every mount:Layout.jsx:761-769—api.getPaletteManifest()intomanifestNavstate (added 2026-06-04 for Collapse sidebar to 8–10 top-level domains + Pinned + Recent #713, when the sidebar had no static access to the manifest).Layout.jsx:771-779—manifestEntryByPathis built from the fetched copy.Layout.jsx:876-889—resolveNavEntrycallsmigrateLegacyNavPath(path, manifestNav)with the fetched copy.Layout.jsx:163— theapiimport exists only for this call.server/routes/palette.js:64-66builds thenavhalf of the response from the very sameNAV_COMMANDSobject the client bundle already contains.Impact
resolveNavEntryreturnsnullfor the 63 manifest entries that have no sidebar presentation (/wiki/log,/goals/tree, …) and for every stored path that must go throughpreviousPathsmigration. Pinned/Recent rows for those paths render nothing on first paint and pop in later, anduseNavWorkingSet's normalize effect (client/src/hooks/useNavWorkingSet.js:96-107) re-runs when the fetch lands.client/src/components/Layout.test.jsx:71-78has to mockgetPaletteManifestwith a hand-written one-entry manifest, so the tests exercise a fake manifest rather than the real one.Fix
Use the static import for everything:
manifestNavstate. BuildmanifestEntryByPathfromNAV_COMMANDS(a module-level map; keep the feature filter inside the existinguseMemoonisFeatureEnabled).NAV_COMMANDStomigrateLegacyNavPathinresolveNavEntry.apiimport fromLayout.jsxand thegetPaletteManifestmock fromLayout.test.jsx; keep the eidoversepreviousPathsassertion, now against the real manifest.Leave
client/src/components/CmdKSearch.jsx:133andclient/src/components/voice/VoiceWidget.jsx:175alone — they need theactionshalf, which only the server can hydrate from the voice tool registry — and leave the endpoint itself as-is (documented for companion apps indocs/COMPANION_APP_API.md:138).Acceptance criteria
Layout.jsxcontains nogetPaletteManifestcall and nomanifestNavstate;git grep getPaletteManifest client/src/components/Layout.jsxis empty./wiki/log) renders its Pinned row on the first render with no network mock — add this case toLayout.test.jsx.previousPaths(the existing eidoverse case) still resolves to the current path on first render without thegetPaletteManifestmock.cd client && npm test -- Layoutpasses;server/lib/navManifest.test.jsandserver/routes/palette.test.jsare unchanged and pass.Scope: small