Context. surface customer · domain discovery · route /explore · auth signed-in · source apps/oshun/web/src/app/explore/page.tsx
Last walked. 2026-06-27 automated coverage reconciliation by Codex — ExploreDashboard component states, mocked taxonomy Playwright, real-BFF /v1/search Playwright, domain-switcher, shell partial-outage banners, shell-state previews, viewport fit, PWA/offline fallback, and docs center generation cross-checked.
Purpose#
A way into anything. Explore is a top-level shell surface that combines a domain/intent-filtered curated discovery feed with debounced full-text search across the six domains. It is the breadth counterpart to the home shell's recency view.
Entry points#
- Shell nav: Explore tab —
WEB_SHELL_ROUTE_PATHS.explore; one of the five primary shell routes (Home, Explore, Activity, Library, Profile) - Search breadcrumb —
/searchbreadcrumbs to "Explore" (seesearch.md); the explore route is the canonical parent for search - Home "See all" rail link — see
home.md - Library focal-stage links that route to
/explore - Domain unavailable redirect — middleware can land users here with
?unavailableDomain=<id>&unavailableReason=<reason>; the sharedShellLayoutrenders the shell-scoped banner and preserves the query across shell tabs.ExploreDashboardstill exposesunavailableDomainIdprops for embedded/direct component use, but/explore/page.tsxdoes not pass route search params into those props. - Hydrated query —
?q=…and?domain=<searchDomain>populate the search input and active domain on first paint viauseBrowserSearchParams() - Direct URL / bookmark — yes; URL state mirrors
qanddomain - Shell state preview —
?shellState=<mode>swaps content forShellSurfaceStatePreviewviaresolveShellRouteSurfaceContent({ surface: 'explore' })
Layout regions#
page.tsx mounts ShellLayout with active="explore" and breadcrumbs
Home → Explore, then renders <ExploreDashboard /> (or the shell-state
preview).
Inside ExploreDashboard:
- Hero / focal stage —
ShellRouteAnchor+ShellLandingFocalStagewith search input, suggestions, hero copy that adapts toactiveIntentanddeferredQuery - Domain tab rail (
DomainTabRail) — All / Tara / Veritas / Nyx / Arete / Nisaba / Metis - Intent filter chips — derived from
EXPLORE_INTENT_FILTERS - Domain breakdown metric pills —
MetricPillper domain when results or curated routes exist - Outage notice —
CustomerShellOutageBannershown when the BFF/v1/searchreturnspartialFailure - Search feed — switches between five
feedStatevalues:curated | loading | results | empty | idle - Curated discovery rail —
CuratedCards rendered when no query - Collections, concept entry points, spotlights —
filteredCollections,EXPLORE_CONCEPT_ENTRY_POINTS,EXPLORE_SPOTLIGHTS, all filtered by domain + intent - Domain briefing panel (
DomainBriefingPanel) — per active domain (getExploreDomainSection) - Secondary collapsible panel (
secondaryOpen) — additional collections, concept entries, spotlights - Saved state badges — derived from
useOshunWebLibraryStore; library save toggles available per result
States#
- Idle (no query, curated visible) —
feedState === 'curated'; curated cards, collections, concept entries, and spotlights render. Covered byExploreDashboard.test.tsx,explore-blended-discovery.spec.ts, andviewport-fit.spec.ts. - Loading (debounced) —
feedState === 'loading';LoadingStateshown after the 260 ms debounce whilefetch(/v1/search)is in flight. Covered byExploreDashboard.test.tsx. - Results populated —
feedState === 'results'; results filtered by intent. Covered byExploreDashboard.test.tsx,explore-blended-discovery.spec.ts,nisaba-search-filters.spec.ts, andexplore-real-search-continuity.spec.tsagainst the real local BFF. - Empty results —
feedState === 'empty'after a successful fetch that returns no results. Covered byExploreDashboard.test.tsx. - Error / abort —
fetchfailure (non-AbortError) clears results and outage notice; surfaces no toast in this branch. Covered byExploreDashboard.test.tsx. - Partial failure (search outage) —
payload.partialFailure === truetriggersCustomerShellOutageBannerwith degraded-domain list and retry control. Covered byExploreDashboard.test.tsxandshell-partial-outage-banners.spec.ts. - Unavailable domain redirect —
/explore?unavailableDomain=...shows the shell-scoped partial outage banner throughShellLayout; the dashboard prop remains defaulted for the route. Covered byshell-partial-outage-banners.spec.tsandshell-domain-unavailable-fallback.spec.ts. - Saved-item state — items in the user's Library store show "Saved"
affordance (via
savedItemKeys). Covered byExploreDashboardSearchTelemetry.test.tsxsave/unsave assertions andexplore-real-search-continuity.spec.ts, which saves a live Nisaba result from/explore, waits for the real saved-items BFF write, verifies persisted metadata, and hydrates the saved row from a clean second-device/library. - Shell state preview —
?shellState=swaps the route content for the preview component. Covered byshell-surface-states.spec.ts. - Reduced motion —
disableAnimationprop disables card scroll-in / filter chip transitions; Playwright fixtures also emulate reduced motion suite-wide. Covered byExploreDashboard.test.tsxand the Explore E2E specs that import./fixtures.
Interactions#
Hero search input#
- Search input (text input inside
ShellLandingFocalStage)- Function: updates
query;useDeferredValuedebounces; mutates URL viawindow.history.replaceState(no Next router push) - Keyboard: standard text input
- Sends
/v1/search?q=&domain=with 260 ms debounce - Coverage:
ExploreDashboard.test.tsx,explore-blended-discovery.spec.ts,nisaba-search-filters.spec.ts, andexplore-real-search-continuity.spec.ts.
- Function: updates
- Search suggestion chips — clicking sets
queryto suggestion text. Covered byExploreDashboard.test.tsx. - Hero focal links (
Current handoffs) — typed into the focal rail fromShellLandingFocalCard; hrefs are built throughbuildActionHref. Covered byExploreDashboard.test.tsx.
Domain tab rail#
- Each domain tab (button)
- Function: sets
activeDomain; updates URLdomainparam; triggers re-fetch - Aria/role: tab list inside
WorkspaceSegmentRail-style component - Coverage:
ExploreDashboard.test.tsx,domain-switcher.spec.ts, andexplore-real-search-continuity.spec.ts.
- Function: sets
- Domain cue button (
DomainCueButton) — domain affordance below tabs. Covered byExploreDashboard.test.tsxandexplore-blended-discovery.spec.ts.
Intent chip rail#
- Each intent chip (
FilterChip)- Function: sets
activeIntent(returns to/away from'all') - Active state visible by
data-activeattribute - Coverage:
ExploreDashboard.test.tsxandexplore-blended-discovery.spec.ts.
- Function: sets
Curated card / collection / spotlight / concept entry#
- Curated card click (Link) — navigates to constructed href via
buildExploreDomainHref. Covered byExploreDashboard.test.tsxandexplore-blended-discovery.spec.ts. - Library save toggle (when result is library-saveable) — calls
toggleOshunWebLibraryItem(buildLibraryItemFromSearchResult(...));trackLibraryItemSaved/trackLibraryItemUnsavedfires. Covered byExploreDashboardSearchTelemetry.test.tsx; the signed-in real-BFF write and clean-device Library hydrate are covered byexplore-real-search-continuity.spec.ts. - Search result open —
trackSearchResultOpenedfrom@/analytics/searchResultTelemetry(per result open). Covered byExploreDashboardSearchTelemetry.test.tsx.
Secondary panel#
- Expand/collapse button — toggles
secondaryOpen; revealssecondaryCollections,secondaryConceptEntries,secondarySpotlights. Covered byExploreDashboard.test.tsxandprogressive-disclosure.spec.ts.
Domain briefing panel#
- Domain action links (
DomainActionLink) — domain-specific deeper routes; targets vary pergetExploreDomainSection(activeDomain). Covered byExploreDashboard.test.tsxandexplore-blended-discovery.spec.ts. -
DomainQuickLink— quick-navigation tile to the active domain root. Covered bydomain-switcher.spec.ts.
Outage banner#
- Retry search — section-scoped outage banner primary action increments
searchAttemptand reissues the same/v1/searchrequest. Covered byExploreDashboard.test.tsx; shell-level outage banner copy is covered byshell-partial-outage-banners.spec.ts.
Data & contracts#
- Reads:
- BFF
GET /v1/search?q=<query>&domain=<domain>(viabuildOshunBffUrl) — returnsExploreSearchPayloadwithresults,partialFailure,errors,domainStatus; direct client calls authenticate withresolveBffAuthToken() - Local discovery models:
EXPLORE_CURATED_ITEMS,EXPLORE_COLLECTIONS,EXPLORE_CONCEPT_ENTRY_POINTS,EXPLORE_SPOTLIGHTS,EXPLORE_QUERY_SUGGESTIONS,EXPLORE_INTENT_FILTERS useOshunWebLibraryStore()— saved-item snapshot
- BFF
- Writes:
toggleOshunWebLibraryItemtoggles the library store. For signed-in members the store pushes non-demo saves to/v1/library/saved-items/<domain>/<itemId>and hydrates them back on a clean device.
- Realtime: None.
- Caching: client-side state for query/intent/domain; URL mirrors query and domain; no SWR cache observed
- Auth/role check: relies on shell middleware plus the BFF auth pre-handler
on
/v1/search - Telemetry:
trackSearchResultOpened(search-result clicks)trackLibraryItemSaved/trackLibraryItemUnsaved
Cross-references#
- Shell:
shell/01-app-shell.md - Sibling routes:
search.md— dedicated search results view (also issues/v1/search)home.md— recommendations rail leads herelibrary.md— focal-rail shortcut targetactivity.md,messages.md,switcher.md
- Component sources:
apps/oshun/web/src/components/explore/ExploreDashboard.tsxapps/oshun/web/src/components/explore/exploreDiscoveryModels.tsapps/oshun/web/src/components/search/search-config.ts
- Feature spec:
V1/features.md - Architecture:
V1/ARCHITECTURE.md
Open questions / known gaps#
-
?unavailableDomain=is handled byShellLayout, not passed from/explore/page.tsxintoExploreDashboard; documented above. - The section outage banner has an explicit "Retry search" action wired to
searchAttempt; covered above. - The client no longer hard-codes the search bearer at the call site; it
calls
resolveBffAuthToken()and the BFF still enforces auth/scope. - Explore has no internal dashboard offline branch; route-level offline and
cached fallback behavior is covered by
pwa-install-update-offline.spec.tsandshell-surface-states.spec.ts.