---
status: reconciled-partial
coverage_depth: deep-for-separated-search-room-workspace-and-fixture-seams
last_walked: 2026-05-29
last_reconciled: 2026-08-21
specs:
  - apps/oshun/web/e2e/nisaba-scholarly-read.spec.ts
  - apps/oshun/web/e2e/nisaba-desk-deepening.spec.ts
source: WALKTHROUGH/journeys/nisaba-scholarly-read.md
---

# Journey result: Nisaba scholarly read — three passage identities, no continuous study record

- **Walked**: 2026-05-29 at `9455bc53ad`. The retained result cited an ephemeral
  `/tmp/claude-oshun-lt-2-walkthrough/walk.mjs`, saw `/nisaba` and
  `/nisaba/scholar` headings, and confirmed that scholar, lexicon, and graph
  routes returned `200`. It retained no script, trace, account, selected passage
  id, annotation, notebook write, plan write, cache entry, or read-back.
- **Reconciled**: 2026-08-21 against the real universal-search route and seed,
  shell launch resolver, parameterized Nisaba passage workspace, authenticated
  compact-room route and server component, room view-model, passage actions,
  legacy depth fixtures, local study stores, service-worker policy, six focused
  unit/source suites, and both named Playwright files. The documented re-run
  below is a focused repository and Docs Center verification, not a production
  account, deployed browser, shared-device, or cross-device exercise.
- **Verdict**: **partial / deep for the separated shipped surfaces** — search
  can open the real Epictetus passage in the live BFF-backed workspace; the
  compact room renders the daily Dhammapada passage with its matching annotation
  count; and the legacy pages expose substantial static scholarly specimens.
  They are not one scholarly-read transaction. The compact room still saves
  under the legacy Marcus id, while the depth pages use that Marcus Aurelius
  composition with display-only controls.
- **Current authority**:
  [`WALKTHROUGH/journeys/nisaba-scholarly-read.md`](../journeys/nisaba-scholarly-read.md)
  now names the three passage identities, the real versus assisted browser
  seams, and the server, local, fixture, and offline-cache boundaries.

## Result at a glance

| Interaction             | Proof · limit                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Historical walk         | **Proof:** the compact desk and three depth routes rendered, the desk had an `h1`, and language controls were later wired. **Limit:** route reachability did not preserve a selected passage, click a toolbar action, create a notebook item, add a plan, or establish continuity among the pages.                                                                                                                                                                                                                                                                                   |
| Search result           | **Proof:** the real `/v1/search` response supplies the `nisaba-passage-speech` seed and its `/library/passage/nisaba-passage-speech` target. Search launch wraps that target into `/domains/nisaba?...path=...`, and the named browser case opens the parameterized workspace. **Limit:** its browser search client has no in-memory token in this harness; the spec intercepts the GET, reissues it to the real BFF with a generated dev bearer, and fulfills with that response. This proves ranking and launch against a real response, but not the unassisted browser-auth seam. |
| Live workspace          | **Proof:** the requested-passage hook fetches `/v1/nisaba/passages/nisaba-passage-speech`; the browser asserts **On Guarded Speech**, **Enchiridion XXXIII**, primary text, study signals, quick actions, and twelve reading-action affordances. **Limit:** the named journey asserts their presence, not annotation creation, notebook capture, study-plan creation, persistence, reload read-back, or offline replay. Those behaviors belong to other Nisaba suites and authorities.                                                                                               |
| Compact `/nisaba` room  | **Proof:** the server component forwards the `oshun-access` cookie as a bearer, reads `/v1/nisaba/room`, and renders an honest unavailable state for null or malformed data. The authorized route builds the room from the current daily passage: **Mind Precedes All States**, Dhammapada I.1–2, its English body, related concept, and compare targets. **Limit:** it is a daily-passage resolver, not the search-selected Epictetus workspace.                                                                                                                                    |
| Room annotation count   | **Proof:** the route reads the owner-scoped annotation list for the daily passage and passes that same passage into the room builder. The visible title, body, and **Annotations** count are now identity-aligned. **Limit:** **Cross-references: 4** and **Saved excerpts: 2** remain authored constants rather than computed totals.                                                                                                                                                                                                                                               |
| Compact passage actions | **Proof:** Save writes a browser Library item; Read aloud uses `speechSynthesis`; flagging passes through the trust-and-safety control. **Limit:** all three receive the hard-coded id `nisaba-on-the-ruling-part` while the visible title and citation come from the daily Dhammapada passage. A save can therefore display the Buddhist title while resuming `/library/passage/nisaba-on-the-ruling-part`.                                                                                                                                                                         |
| Language and sections   | **Proof:** the room exposes only the language carried by the daily passage, so the seeded English record no longer advertises unavailable Pali or side-by-side text. **Limit:** the fixed footer section controls still link to `#section-I` and `#section-III` even though a dynamic passage has no authored section apparatus or persisted reading position.                                                                                                                                                                                                                       |
| Legacy scholar chain    | **Proof:** scholar, lexicon, manuscript, notebook, and plan routes render a detailed Marcus Aurelius / `hēgemonikon` specimen; the named browser case checks apparatus, witnesses, lexicon anatomy, folio anatomy, no-index notebook metadata, the seven-week arc, and browser Back. **Limit:** all five read `nisaba-depth.ts` fixtures. **Reader mode**, **Edition**, and **Cite** have no handler or destination; lexicon arrows are text; the notebook is Renata K.'s display-only composition; and the plan has no completion control.                                          |
| Legacy privacy          | **Proof:** the fixture notebook emits `noindex, nofollow` and presents **private to you** copy. **Limit:** it accepts no notebook id or subject and performs no authorization check. Robots metadata governs discovery, not access.                                                                                                                                                                                                                                                                                                                                                  |
| Offline continuity      | **Proof:** daily-passage and notebook-list responses have 24-hour localStorage snapshots; the service worker separately stale-while-revalidates exactly `/v1/nisaba/passages/daily` and `/v1/nisaba/notebooks`, refuses expired cache while offline, and purges member caches on the normal session-clear path. **Limit:** Cache API and localStorage keys are URL/status scoped, not member scoped. Correct isolation relies on the session marker and purge; requested passage detail, room, annotations, manuscript, and legacy pages are outside the API allowlist.              |
| Named automation        | **Proof:** `nisaba-scholarly-read.spec.ts` has 11 browser cases and `nisaba-desk-deepening.spec.ts` has five real-HTTP room cases. Unit coverage separately pins the room builder, SSR ready/unavailable paths, language switching, browser Library action, offline snapshots, hook hydration, and service-worker expiry policy. **Limit:** the 16 named browser/API cases are surface and contract evidence distributed across distinct passages; they do not execute one search → read → annotate → notebook → plan → reload chain.                                                |

## Evidence map

Solid arrows show a real handoff within one surface. Dashed arrows mark where a
label, action id, navigation, or cache crosses into another passage or
authority. The three titles are deliberately visible: treating them as one
record was the central error in the historical pass.

```mermaid
flowchart TB
    J[Nisaba scholarly read] --> S[Real search response]
    J --> C[Compact /nisaba]
    S --> W[Live workspace<br/>Epictetus · Enchiridion XXXIII]
    D[Daily passage authority<br/>Dhammapada I.1–2] --> C[Compact /nisaba room]
    C -. hard-coded save and flag id .-> A[nisaba-on-the-ruling-part]
    C --> L[Legacy route navigation]
    L --> M[Static depth fixtures<br/>Marcus Aurelius · Meditations IV]
    M --> N[Display-only Renata notebook<br/>and seven-week plan]
    W -. no selected-passage handoff .-> C
```

## Proven observations

### The historical pass proved repaired routes, not a scholarly lifecycle

- The old result was right that `/nisaba/passage/sample` was a walker-invented
  path rather than a broken product link. It was also right that the desk gained
  a real heading and that its language chips became buttons.
- Its `pass` verdict then promoted route render and link reachability into a
  journey outcome. Nothing retained from that walk connected the search result
  to the compact room, the room to the scholar text, the scholar text to the
  Renata notebook, or the notebook to the plan.
- The current source provides stronger positive evidence for every independent
  page. It simultaneously shows why the joined claim remains partial.

### Search reaches a real parameterized passage, through an assisted auth seam

- Universal search has a concrete `nisaba-passage-speech` seed with title **On
  Guarded Speech**, reference **Enchiridion XXXIII**, public-domain source
  attribution, excerpt, and target path. The generic result launcher preserves
  that path as the domain shell's encoded `path` parameter.
- `NisabaSurface` resolves the path, compares it with the daily id, calls
  `useNisabaPassage(requestedPassageId)`, and uses the returned passage when the
  id matches. That is a real detail-resolution path rather than a client-only
  title substitution.
- The named test's session route does not populate the search component's
  in-memory API token. `forwardSearchWithAuth` therefore intercepts only the
  main `/v1/search` GET, calls the real BFF with a ten-minute dev bearer, and
  forwards its status and body. Assertions on the real Nisaba result and its
  launch are valuable, but the result must not call that an unassisted
  production-browser request.
- After launch, the test checks the workspace anatomy and twelve action
  affordances. It never operates those annotation, notebook, collection,
  study-plan, citation, or scholar controls, so their presence is not a write.

### The compact room now follows the daily passage, with one action-id seam

- `bffGet` now correctly awaits Next's cookie jar and forwards `oshun-access` as
  `Authorization: Bearer`; authenticated responses use `no-store`. The stale
  comment in `nisaba-desk-deepening.spec.ts` that claims cookie forwarding is
  infeasible predates that implementation. The current named journey plants the
  access cookie and can render the live room.
- `getRoomHandler` asks `getDailyPassage(userId)`, lists annotations for that
  passage id, obtains its compare context, and passes the daily title,
  reference, source, language, body, concepts, and compare targets to
  `buildNisabaRoom`. The seeded room is therefore **Mind Precedes All States**,
  Dhammapada I.1–2, and its annotation count belongs to the visible passage.
- The builder derives paragraphs, a concept layout, and related compare rows
  from that record. It exposes only the record's available language and omits
  the curated SN 22.59 section apparatus. Cross-references `4` and saved
  excerpts `2` remain authored marginalia constants.
- The footer retains one identity error. `NisabaPassageActions` receives the
  visible daily title and citation but a constant `nisaba-on-the-ruling-part`
  id. Its unit test proves that exact id becomes the Library resume path. The
  resulting row can therefore combine one passage's id with another passage's
  title and citation.
- Read aloud gathers up to 4,000 characters from `main p` rather than carrying a
  canonical passage body and edition id. It is a convenience playback of
  rendered prose, not a source-pinned audio artifact.

### Language and section controls are intentionally narrow

- The builder exposes only the source record's language, so the seeded English
  passage has one truthful language button instead of ineffective Pali and Both
  choices. No language preference is written to a URL or durable store.
- Dynamic daily passages have no fabricated section list. The shared reading
  footer still links to `#section-I` and `#section-III`, however, so those
  hashes are presentation affordances without matching section records.
- Related rows come from the daily passage's compare targets. They do not hand a
  newly selected passage back into the compact room.

### Legacy depth pages are authored specimens with a third text identity

- The Scholar page moves from the Buddhist room to Marcus Aurelius,
  _Meditations_ IV.viii–xii. Its four marginal notes, four witnesses, grouped
  cross-references, and five concepts are returned by a zero-argument fixture
  function. The toolbar wraps ordinary buttons without `href` or `onClick`.
- Lexicon, manuscript, notebook, and plan repeat that authored thread. The
  lexicon's neighboring words are masthead text, not anchors; the manuscript is
  a rendered facsimile specimen; and the notebook always names Renata K. and
  **On the ruling part** regardless of account or prior passage.
- The plan always reports week four and 18 of 31 readings from a seven-week
  fixture. There is no add-to-plan handoff from search, no completion action, no
  member record, and no reload read-back in the named test.
- Browser Back from Scholar to `/nisaba` proves navigation history. It does not
  carry the Marcus apparatus into the daily room or reconcile either with the
  Epictetus workspace.

### Offline support is deliberately narrow and depends on cleanup

- Client hooks persist daily passage and notebook-list payloads under shared
  localStorage keys for at most 24 hours; recents are capped at six. Notes,
  drafts, collections, study plans, and density mode use other browser-local
  keys and do not become BFF records through that persistence module.
- The service worker caches two authenticated read URLs by request URL. Its own
  comments correctly acknowledge that Cache API matching ignores authorization
  and that authenticated caching can leak across sessions on a shared browser.
  The mitigation is an `oshun.pwa.member-cache-present` marker plus
  `OSHUN_CLEAR_MEMBER_DATA` on normal session clear, which deletes BFF, shell,
  media, and queued-action stores before re-precaching public shell assets.
- This is a cleanup-dependent boundary, not intrinsic subject partitioning. The
  docs should not promise shared-device isolation without verifying the
  sign-out/session-clear path, nor claim that a cold or non-allowlisted passage
  is available offline.

## Boundaries and gaps

- **A search result is not the compact desk.** The selected Epictetus id reaches
  `/domains/nisaba`; `/nisaba` independently resolves the daily Dhammapada room.
- **A visible passage is not the saved passage identity.** The footer stores and
  flags `nisaba-on-the-ruling-part` while displaying the daily passage.
- **A hash is not reading-position state.** Footer hashes have no matching
  section records for a dynamic daily passage.
- **A toolbar label is not an action.** The legacy Scholar buttons render but
  have no handler or destination.
- **`noindex` is not authorization.** The Renata notebook fixture neither
  accepts a member id nor checks ownership.
- **A warm cache is not a subject-partitioned cache.** URL-keyed cached reads
  and browser-local snapshots rely on session cleanup rather than per-user keys.
- **Sixteen cases are not one continuous scholarly read.** The named automation
  proves search, page anatomy, routing, presentation, and room HTTP gates; it
  does not create and read back one annotation, notebook item, or plan for the
  same passage.

## Re-run evidence

- `python3 -m pytest tools/docs_center/tests/test_generator.py -q` finished with
  **70 passed**. The Result 43 source assertion pins registry position 43, front
  matter, section order, one evidence map, the 11 + 5 named case counts, search
  auth forwarding, all three passage identities, the remaining action-id
  mismatch, static toolbar, and URL/status-scoped offline stores.
- Docs generation produced **3,242 files / 2,913 reader pages**; the freshness
  check accepted all 3,242. Integrity reported **0 structural broken links / 0
  dead source paths** across 3,505 entities, with 112 pre-existing imported-
  content warnings surfaced as nonblocking.
- The complete Docs Center reader suite ran serially on desktop and mobile:
  **228 passed and 22 intentionally skipped**. Result 43 checks one `h1`,
  partial status, substantive prose, every current boundary statement, Mermaid
  containment and pairwise node geometry, expand-control clearance, heading
  anchors, source-journey navigation, console/page errors, and horizontal
  overflow. Full-page screenshots were also reviewed at 1,440 px and Pixel 7
  dimensions; no clipping or hierarchy defect was found.

## Source trail

- Search and launch: `apps/oshun/bff/src/routes/universal-search-seeds.ts`,
  `apps/oshun/web/src/components/search/SearchResultsView.tsx`, and
  `apps/oshun/web/src/components/search/search-config.ts`.
- Live passage selection:
  `apps/oshun/web/src/components/domains/NisabaSurface.tsx` and
  `apps/oshun/web/src/lib/hooks/use-bff.ts`.
- Compact room: `apps/oshun/web/src/app/nisaba/page.tsx`,
  `apps/oshun/web/src/lib/lilith-data/nisaba.ts`,
  `apps/oshun/web/src/lib/server/bff-fetch.ts`,
  `apps/oshun/bff/src/routes/nisaba.ts`, and
  `apps/oshun/bff/src/nisaba/room.ts`.
- Passage and daily identities: `apps/oshun/bff/src/nisaba/state.ts`,
  `apps/oshun/bff/src/nisaba/public-domain-corpus.ts`,
  `apps/oshun/web/src/components/lilith/NisabaDeskLanguageSwitcher.tsx`, and
  `apps/oshun/web/src/components/lilith/NisabaPassageActions.tsx`.
- Legacy pages: `apps/oshun/web/src/lib/lilith-data/nisaba-depth.ts`,
  `apps/oshun/web/src/components/lilith/nisaba.tsx`, and the five route pages
  under
  `apps/oshun/web/src/app/nisaba/{scholar,lexicon,manuscript,notebook,plan}`.
- Offline boundaries: `apps/oshun/web/src/lib/nisaba-offline.ts`,
  `apps/oshun/web/src/lib/pwa-service-worker.ts`, and
  `apps/oshun/web/public/sw.js`.
- Named browser evidence: `apps/oshun/web/e2e/nisaba-scholarly-read.spec.ts` and
  `apps/oshun/web/e2e/nisaba-desk-deepening.spec.ts`.

## Cross-references

- [`nisaba-notebook-capture-and-cite.md`](./nisaba-notebook-capture-and-cite.md)
  separates the durable legacy BFF and Studio notebook authorities from local
  notes, generated citations, and the same Renata fixture.
- [`offline-first-time-use.md`](./offline-first-time-use.md) covers the broader
  service-worker warm-cache and session-clear model.
- [`search-explore-deep-read-library-save.md`](./search-explore-deep-read-library-save.md)
  covers cross-domain search and Library launch semantics.
- [`veritas-to-nisaba-deeper.md`](./veritas-to-nisaba-deeper.md) covers another
  route into the live Nisaba domain workspace.

## Open questions

- Should `/nisaba` accept a canonical passage id and render the same BFF passage
  selected by search, or remain a deliberately separate daily surface?
- Should the room API return its canonical passage id so Save, Read aloud, and
  Flag can use it rather than `nisaba-on-the-ruling-part`?
- Should the legacy depth pages be wired to the live workspace, retired, or
  visibly labelled as authored specimens?
- Which live annotation/notebook/plan mutations define the product's actual
  scholarly-read completion event, and what same-record read-back should gate
  it?
- Should offline cache and local snapshot keys include a stable subject
  partition so privacy does not depend solely on session-clear cleanup?
