---
status: reconciled-partial
coverage_depth: partial
last_walked: 2026-05-29
last_reconciled: 2026-07-18
specs:
  - apps/oshun/web/e2e/library-collections-new.spec.ts
  - apps/oshun/web/e2e/library-save-collection-share.spec.ts
  - apps/oshun/web/e2e/library-collection-management.spec.ts
  - apps/oshun/web/e2e/collection-share.spec.ts
  - apps/oshun/web/e2e/library-shared-resolution.spec.ts
  - apps/oshun/web/e2e/library-shared-web-view.spec.ts
source: WALKTHROUGH/journeys/library-save-collection-share.md
---

# Journey result: Library save, collection, and share

- **Walked**: 2026-05-29 at `bf12b0f7d8c291499e1df1ec75d4bd21acf3f6b1`. The
  retained walker was an ephemeral `/tmp` script. It rendered `/library`,
  `/library/collections`, and `/library/collections/new`, then observed a
  collection-create POST.
- **Reconciled**: 2026-07-18 against the current journey, all four Library
  routes, the saved-item and collection sources, the sharing engine/BFF, the
  recipient route, and six focused browser specs. This was source review, not a
  fresh application run.
- **Verdict**: **partial** — the dated route/create slice passed and later
  browser suites substantially deepened save, collection editing, share-policy,
  and recipient-denial evidence. The complete journey still spans two
  disconnected collection models, keeps dashboard collection composition on one
  device, and resolves shared permission without rendering the sender's
  collection contents.
- **Current authority**:
  [`WALKTHROUGH/journeys/library-save-collection-share.md`](../journeys/library-save-collection-share.md)
  and its `partial` coverage-registry row.

## Result at a glance

| Evidence plane                   | Current implementation                                                                                                                           | Strongest surviving proof                                                                                                                   | Authority limit                                                                                                                                         |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Saved items                      | Domain saves converge on `webLibraryStore`; signed-in snapshots use `/v1/library/saved-items`, with optimistic local cache and guest migration   | Real-BFF save/read-back, unsave, same-user clean-device hydrate, LWW sync, tombstones, isolation, cap paywall, and several direct producers | Not every producer or entitlement/resume permutation is exercised; Nyx event save still owns a separate local action store                              |
| Server collection catalog        | `/library/collections/new` posts label + opaque item ids to the member-scoped `/v1/library/collections`; `/library/collections` lists those rows | Live-BFF accepted, pending, validation, offline, focus, owner-isolation, and mobile form coverage                                           | Create/list is a separate plane from the dashboard editor; the success/list links point to `/library?collectionId=…`, which the root page does not read |
| Dashboard collection composition | `/library/collection/:id` edits label, description, follow-up, membership, and archive state in `oshun.library.collections.v1`                   | Create-from-filter, edit, membership toggle, archive, corrupt fallback, unknown deep link, and v0→v1 migration                              | Browser-local only; labels, membership, and archive state do not follow the member to another device                                                    |
| Share policy                     | `CollectionShareControls` writes private, named-user, link, and public-profile policy through `/v1/library/collections/:id/*`                    | Token issue/revoke, invite/referral attribution, permission tiers, owner checks, and policy read-back over the BFF                          | The policy store is process memory, and policy creation does not prove that a corresponding server collection payload exists                            |
| Recipient route                  | `/share/:token` asks the BFF to resolve a collection id and requested permission                                                                 | Valid grant, wrong/revoked token, insufficient tier, and same-id local-shadow rejection                                                     | Current tests use signed-in recipients; the page shows id/tier/via only, not sender labels, items, provenance, or embed cards                           |

## Evidence map

The current UI crosses three persistence planes. The dashed branch is the key
disconnect a green create response cannot reveal.

```mermaid
flowchart LR
    A[Domain Save] --> B[Saved-items BFF]
    B --> C[Library hydrate]
    C --> D[Dashboard collection editor]
    D --> E[Local collection payload]
    D --> F[Share-policy BFF]
    F --> G[Share token]
    G --> H[Recipient permission view]
    I[Server collection form] --> J[Server collection catalog]
    J -. collectionId query ignored .-> C
    H -. contents not delivered .-> E
```

## Proven observations

### The dated slice is reproducible as a historical claim

- Both abbreviated commits in the old record resolve in repository history: the
  walk at `bf12b0f7d8c291499e1df1ec75d4bd21acf3f6b1` and the earlier route
  repair at `8ad5d6a4dfac400ef925ef95df2d6bf155c57158`.
- All three dated routes remain in the current 701-route inventory. The new
  collection form still POSTs `{ label, itemIds }` to `/v1/library/collections`,
  exposes pending/error/accepted states, preserves fields after server/offline
  failure, and moves focus to its accepted status.
- The member-scoped BFF collection route rejects anonymous callers, validates a
  two-character label, records the owner, returns `202`, and lists only the
  caller's rows plus legacy unowned migration rows. Its backing tier can be
  PostgreSQL, a protected development snapshot, or test memory.
- The original “0 page errors, 0 persistent failures” remains a dated walker
  observation. The missing `/tmp` walker means its exact selectors, environment,
  and raw report cannot be independently inspected now.

### Saved-item continuity is substantially deeper today

- `webLibraryStore` keeps an offline cache but hydrates signed-in members from
  the saved-items BFF. Focused browser evidence covers direct Nisaba passage
  save, guest→sign-in migration, clean-device hydration, resume navigation,
  saved-queue transitions, and unsave row removal.
- Companion real-HTTP suites cover same-user cross-device propagation, LWW
  conflict accounting, tombstone deletion, per-user isolation, and the 25-save
  free-tier cap. Search, Explore, Veritas, Sophia, Atelier, Tara, Nyx search
  results, and Metis producers have targeted evidence elsewhere in the current
  journey registry.
- This evidence is appropriately stronger than “the page rendered.” It proves
  server read-back for named producers and states; it does not prove every
  saveable surface or every canonical resume destination.

### Collection composition and sharing are not one persisted object

- The Dashboard collection manager builds `/library/collection/:id` links and
  persists its own versioned label, description, follow-up, membership, and
  archive model under `oshun.library.collections.v1`.
- The separate server catalog links a created row to
  `/library?collectionId=<id>`, while `app/library/page.tsx` only parses
  `domain` and `kind`. That link opens the generic Library rather than selecting
  the server-created collection. The form spec currently asserts the href, not a
  successful open-and-edit continuation.
- `CollectionShareControls` can attach a valid BFF policy to a Dashboard
  collection id, but the sharing route governs authorization only. It does not
  fetch or deliver the Dashboard's local collection record.
- The recipient route deliberately avoids the recipient's same-id local shadow.
  Its granted state displays the server-resolved collection id, tier, and
  resolution channel; this is a correct isolation repair, not content sharing.

### Share authorization fails closed within its tested scope

- Tokens are generated from 32 random bytes, returned once, and stored as a
  SHA-256 hash. The engine validates view/comment/copy ordering, expiry,
  revocation, owner-only mutation, named-user grants, and public-profile policy.
- Browser/BFF specs cover issue→resolve→revoke, wrong-token denial, insufficient
  permission, invite/referral attribution, private state, named-user copy, and
  public-profile comment policy read-back.
- The current `LibrarySharePolicyStore` is an in-process `Map`. A BFF restart or
  a different instance loses its policies unless a durable adapter replaces this
  binding; “real BFF” must not be read as production durability.

## Boundaries and gaps

- **The dated pass belongs to a route/create slice, not the current journey.**
  Rendering three pages and observing one POST never proved domain save,
  collection editing, link authorization, recipient isolation, or read-back.
- **A server collection row is not the Dashboard collection.** The create/list
  route, local editor, and share policy currently agree only on an opaque id;
  they do not share one authoritative collection payload.
- **The “Open it” continuation is broken by contract.** Both the catalog and
  accepted form emit `?collectionId=`, but `/library` ignores that parameter.
- **A permission card is not shared content.** The recipient does not receive
  the sender's label, member items, ordering, provenance, grounding, synthetic
  indicators, or embeddable card.
- **A process-local policy is not durable authorization.** Restart and
  multi-instance behavior remain outside the present sharing proof.
- **Anonymous link access is not proven.** The resolution BFF route requires an
  auth context, and current browser evidence signs in the recipient.
- **Collection composition is not cross-device.** Saved items sync; Dashboard
  collection metadata and membership remain localStorage-only.
- **Several journey branches remain open.** Nyx event-save convergence,
  filter-URL write-back, offline resume, gated-item resume, collection delete,
  public-profile collection rendering, and provenance-preserving embed cards are
  not closed by this result.
- **Source reconciliation is not execution.** None of the cited web/BFF suites
  was rerun for this documentation audit.

## Re-run evidence

Run the live BFF and production-like web app at the same immutable commit, then
execute the focused specs one at a time with one worker:

```bash
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/library-collections-new.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/library-save-collection-share.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/library-collection-management.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/collection-share.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/library-shared-resolution.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/library-shared-web-view.spec.ts --project=chromium --workers=1
```

Retain the web/BFF commit, backing-store tier, environment manifest, Playwright
artifacts, and member identities. Add an explicit create→open→edit→second-device
read-back assertion before upgrading this result beyond partial.

## Source trail

- [Current journey contract](../journeys/library-save-collection-share.md)
- [Current coverage registry](../journeys/coverage.md)
- [Server collection list](../../apps/oshun/web/src/app/library/collections/page.tsx)
- [Server collection form](../../apps/oshun/web/src/app/library/collections/new/LibraryNewCollectionForm.tsx)
- [Library root route](../../apps/oshun/web/src/app/library/page.tsx)
- [Dashboard collection manager](../../apps/oshun/web/src/components/library/LibraryDashboard.tsx)
- [Saved-item store](../../apps/oshun/web/src/lib/library/webLibraryStore.ts)
- [Collection share controls](../../apps/oshun/web/src/components/library/CollectionShareControls.tsx)
- [Shared-token route](../../apps/oshun/web/src/app/share/%5Btoken%5D/page.tsx)
- [Collection catalog BFF routes](../../apps/oshun/bff/src/routes/domain-stubs.ts)
- [Sharing BFF routes](../../apps/oshun/bff/src/routes/library-sharing.ts)
- [Share-policy store](../../apps/oshun/bff/src/library/share-policy-store.ts)

## Cross-references

- [Library route evidence](../customer/02-home-discovery/library.md)
- [Collection deep-link evidence](../customer/02-home-discovery/library-collection-collectionId.md)
- [New collection route evidence](../customer/02-home-discovery/library-collections-new.md)
- [Shared-token route evidence](../customer/02-home-discovery/share-token.md)
- [Search→deep read→save journey](../journeys/search-explore-deep-read-library-save.md)
- [External collection contract](./external-dependencies/library-collection-contract.md)
- [V1 completeness audit](./v1-completeness-audit-2026-06-22.md)

## Open questions

- Which collection model becomes canonical: the BFF catalog, the Dashboard local
  payload, or a merged server contract with migration from both?
- Should server-created collection links use `/library/collection/:id`, and what
  server read must hydrate that route before editing is enabled?
- When will share policies move from the in-process map to the same durable,
  owner-scoped collection boundary?
- What payload should an authorized recipient receive, and how will it preserve
  ordering, provenance, grounding, synthetic indicators, and entitlement gates?
- Is anonymous link-with-key access intended? If so, what limited auth/rate/
  revocation boundary replaces the current signed-in precondition?
- Which test will prove one continuous domain-save→collection-create→share→
  recipient-content flow with clean sender and recipient browser contexts?
