# Journey: Search → Explore → deep-read → library → save

This is one of the repository's strongest cross-view customer joins. Search and
Explore share the live `/v1/search` contract; save actions from both surfaces
write the same authenticated saved-items API; Library can hydrate without its
browser cache in a second context and resume several canonical destination
types. Saved **queries** are a different contract and remain browser-local.

## Personas

- **Researcher** — filters `/search`, previews evidence, opens a domain object,
  and saves it before or after deep reading.
- **Explorer** — begins with curated `/explore`, issues a live query, and joins
  the same result/save contract.
- **Returning reader** — opens `/library` in a clean browser context and resumes
  the saved canonical object.
- **Saved-search user** — preserves a query/filter workspace for reuse in the
  same browser profile.

## Pre-conditions

- The customer is signed in and the BFF grants the requested domain scope.
- `/v1/search` returns `SearchResultItem` records with stable domain, kind, id,
  score, and launch metadata.
- The authenticated Library saved-items routes are reachable. LocalStorage is a
  cache for saved **items**, not their production source of truth.
- The target domain route understands the `origin` and `path` produced by
  `buildSearchResultLaunchPath` / `buildLibraryResumeHref`.

## Steps

### 1. Search and filter the live result set

`/search` initializes from `?q=` and the advanced URL state, then calls
`GET /v1/search?q=&domain=`. Domain, kind, sort, score, saved-only, and match
scope round-trip through the URL. A submitted query is also added to the local
recent-search list. The error branch clears stale results, retains the submitted
query, and offers a same-query Retry action.

### 2. Enter through Explore when useful

`/explore` calls the same BFF for a typed query and rebuilds the result set when
the domain tab changes. Curated cards use `buildExploreDomainHref`; live result
rows use `buildSearchResultLaunchPath`. Both paths preserve an `origin=explore`
handoff rather than pretending the Explore card itself is a deep reader.

### 3. Preview evidence and open the canonical destination

Search preview exposes the result template and grounded-evidence context before
launch. The live-BFF browser matrix proves these destination families. Each path
is wrapped by `/domains/<domain>?origin=explore&path=…`.

| Result              | Proved destination path        |
| ------------------- | ------------------------------ |
| Nisaba passage      | `/library/passage/<id>`        |
| Veritas claim/story | `/claims/<id>` · `/story/<id>` |
| Tara practice       | `/meditate/session/<id>`       |
| Nyx sky event       | `/events/<canonical-slug>`     |
| Metis course        | `/courses/<id>`                |

The Nisaba and Veritas branches assert actual reading/evidence content, not only
the URL or domain hub. Search stores its workspace signature, result key, and
scroll offset before launch and restores the long-list position on Back.

### 4. Save one stable library identity

Eligible Search and Explore rows map the result to one `(domain, itemId)`
Library identity and call `/v1/library/saved-items/:domain/:itemId`. The Nisaba
in-domain save control uses the same identity, so save → in-domain unsave →
resave moves the server row `1 → 0 → 1` instead of creating duplicates. The
Veritas producer also writes the common saved-items contract.

### 5. Hydrate Library and resume in a second context

`/library` reads authenticated saved items from the BFF, applies domain/kind
filters, and builds the resume href from stored metadata. Browser coverage
clears the local Library cache, opens a second context for the same member, and
proves saved-item visibility and destination resume for Nisaba passage, Veritas
claim/story, Tara practice, Nyx sky event, and Metis course. This uses the
saved-items BFF, not Iris preference sync.

### 6. Optionally save the search workspace

“Save this search” persists the query and advanced filters under
`oshun-search-saved-v1` (maximum eight). Recents use `oshun-search-recent-v1`
(maximum six, newest first, case-insensitive promotion). Those browser-local
records reapply URL workspace state; they are not the cross-device Library-item
store. The BFF separately records recent query text for suggestions and exposes
its newest eight.

## Post-conditions

- The selected result opens a domain-specific deep-read surface with the same
  stable object identity.
- A saved item is readable from the authenticated BFF, de-duplicated across
  covered producers, and visible after clearing the local cache.
- The covered destination matrix resumes in a second browser context for the
  same account.
- Recent/saved query workspace state remains local, while recent-query
  suggestions and saved Library items have separate BFF contracts.
- `trackSearchResultOpened`, `trackLibraryItemSaved`, and
  `trackLibraryItemUnsaved` are covered at their actual click/save boundaries.

## Failure modes

- **Domain-scope denial** — a live `403 domain_scope_missing` renders Search
  unavailable, removes stale rows, records the recent query, and retries the
  exact request on demand.
- **Destination drift** — a URL may be structurally valid yet land on a hub or
  blank template. The seeded matrix asserts domain workspace anchors and
  canonical Nyx slug conversion.
- **Producer identity drift** — covered for the Search/Explore/Nisaba paths;
  other non-search producers still need the same item-level de-dup proof.
- **Cold-start deep link** — the server-provided search params initialize the
  workspace without flashing the no-query state.
- **Browser Back** — the Nisaba long-list case restores the same row and scroll
  offset; other destination types are not separately asserted for scroll.
- **Offline chain** — Search has no offline result contract and API reads are
  network-only by default. A Library shell may open while its resume target
  still requires network.
- **Saved-search cross-device assumption** — false; those workspaces are
  localStorage records.

## E2E coverage

- [`apps/oshun/web/e2e/search-saved-and-recent.spec.ts`](../../apps/oshun/web/e2e/search-saved-and-recent.spec.ts)
  carries the live-BFF Search, advanced restore, failure/retry, cold-start,
  scroll, save/de-dup, cache-free Library, second-context, and destination
  matrix.
- [`apps/oshun/web/e2e/explore-real-search-continuity.spec.ts`](../../apps/oshun/web/e2e/explore-real-search-continuity.spec.ts)
  proves live Explore query/tab refetch, shared save metadata, cache-free
  Library hydration, and Nisaba resume.
- [`apps/oshun/web/e2e/explore-blended-discovery.spec.ts`](../../apps/oshun/web/e2e/explore-blended-discovery.spec.ts)
  covers curated/live result reshaping and launch paths.
- [`apps/oshun/web/e2e/search-result-analytics.spec.ts`](../../apps/oshun/web/e2e/search-result-analytics.spec.ts)
  and
  [`apps/oshun/web/e2e/library-save-analytics.spec.ts`](../../apps/oshun/web/e2e/library-save-analytics.spec.ts)
  pin open/save event payloads.
- **Coverage depth: deep** for the seeded matrix; offline resume and every
  possible producer/template permutation remain partial.

## Per-view files touched

- [`customer/02-home-discovery/search.md`](../customer/02-home-discovery/search.md)
  — query, filters, preview, error/retry, and save.
- [`customer/02-home-discovery/explore.md`](../customer/02-home-discovery/explore.md)
  — alternate live-search entry.
- [`customer/02-home-discovery/library.md`](../customer/02-home-discovery/library.md)
  — server hydration, filtering, and resume.
- [`customer/07-nisaba/nisaba.md`](../customer/07-nisaba/nisaba.md) and
  [`customer/05-veritas/veritas-story.md`](../customer/05-veritas/veritas-story.md)
  — representative deep-read destinations.

## Cross-references

- [`library-save-collection-share.md`](./library-save-collection-share.md) —
  collection/share behavior after saving.
- [`multi-device-workspace-handoff.md`](./multi-device-workspace-handoff.md) —
  broader continuity signals; not the saved-item transport here.
- [`sophia-grounded-answer.md`](./sophia-grounded-answer.md) — a result/evidence
  path that can continue into a grounded answer.
- Sources: `SearchResultsView.tsx`, `search-config.ts`, `ExploreDashboard.tsx`,
  `LibraryDashboard.tsx`, and `webLibraryStore.ts` under `apps/oshun/web/src`.

## Open questions

- Which remaining live-BFF object templates and non-search save producers still
  lack destination plus second-context resume proof?
- Should saved query workspaces sync across devices, and if so under which
  account/privacy contract?
- What is the intended offline behavior when Library metadata is available but
  the canonical deep-read route is not warmed?
- `/explore` still needs an explicit contract for `unavailableDomainId` wiring.
