---
status: reconciled-partial
coverage_depth: deep-for-server-backed-core
last_walked: 2026-05-29
last_reconciled: 2026-07-18
specs:
  - apps/oshun/web/e2e/profile-memory-controls.spec.ts
  - apps/oshun/web/e2e/memory-edit-pause-forget.spec.ts
source: WALKTHROUGH/journeys/memory-edit-pause-forget.md
---

# Journey result: Memory review, edit, pause, and forget

- **Walked**: 2026-05-29 against
  `bf12b0f7d8c291499e1df1ec75d4bd21acf3f6b1`. The retained `/tmp` walker is
  ephemeral, so this is a dated smoke record rather than a reproducible run
  artifact. The client-bundle fix later landed in
  `adae8117ddb084fd6dbf9c26b0a31e4f86da2148`; the historical `/v1/memory`
  stub landed in `03d45f0ad7a276369d9536f8f15e7ba0c627d945`.
- **Reconciled**: 2026-07-18 against the current profile component, browser
  reducers, Iris BFF route and durable adapter, startup wiring, and both named
  Playwright specs. This was source review, not a fresh application run.
- **Verdict**: **partial / deep for the server-backed core** — the current
  suites deeply cover Iris hydration, server-authoritative pause, blocked and
  resumed writes, single-record deletion, and an export receipt. Editing,
  conflict resolution, the audit projection, and the seven-day full-clear
  request remain browser-local; the destructive challenge is not server-issued.
- **Current authority**:
  [`WALKTHROUGH/journeys/memory-edit-pause-forget.md`](../journeys/memory-edit-pause-forget.md)
  preserves those authority boundaries step by step.

## Result at a glance

| Interaction | Proof · limit |
| ----------- | ------------- |
| Iris hydrate/review | **Authority / proof:** `GET /v1/iris/adapter/review?role=settings&pageSize=100` supplies server records; browser state and `oshun-profile-memory-controls-v1` cache the projection. Real-BFF cases cover hydrate, empty, unavailable/retry, consent, filters, detail, and tenant scope. **Limit:** an unavailable read may leave cached rows visible; detail and filtering do not perform a fresh authoritative read. |
| Edit/conflict | **Authority / proof:** local reducers create revisions; tests cover edit, audit projection, telemetry redaction, and two-tab keep-remote/overwrite. **Limit:** no BFF edit, durable revision precondition, or cross-device compare-and-swap. |
| Pause/resume | **Authority / proof:** signed-in controls call `PATCH /v1/iris/adapter/pause?role=settings`; durable Iris persists the opt-out. Tests prove continuity `off`, `409 iris_memory_paused`, two contexts, resume, and a later write. **Limit:** the second context proves read-back after reload, not real-time push to open devices. |
| Single-fact delete | **Authority / proof:** a synced row sends settings role, hard mode, reason, and `confirmed=true`; durable Iris tombstones the id. Tests prove the real delete, local mirror, and next-review absence. **Limit:** the phrase is static, and the route defaults missing `confirmed` to true instead of validating server-issued step-up. |
| Full clear | **Authority / proof:** request/cancel mutate browser state, localStorage, and telemetry; tests cover the challenge, seven-day banner, and cancellation. **Limit:** no BFF request, durable account-wide schedule, executor, or completion receipt. |
| Export | **Authority / proof:** `POST /v1/iris/adapter/export?role=settings` returns receipt metadata; the browser separately composes the bundle. Tests cover id/count/checksum plus the local artifact and audit chain. **Limit:** the receipt does not make the downloaded artifact a server-owned DSAR export. |

## Evidence map

Solid edges show the customer-facing sequence exercised across the named specs.
Dashed edges name the production authority the current surface does not yet
provide. Only pause/resume and deletion in this sequence mutate durable Iris
authority; edit/conflict and full clear remain local projections.

```mermaid
flowchart TB
    A[Signed-in profile] --> B[Hydrate Iris review, consent, and pause]
    B --> C[Browser control projection and local cache]
    C --> D[Edit and resolve same-origin conflict]
    D --> E[Pause or resume durable writes]
    E --> F[Delete one synced fact]
    F --> G[Request seven-day full clear]
    G --> H[Export receipt and browser bundle]
    D -. missing .-> I[Server edit and revision CAS]
    F -. missing .-> J[Server-issued destructive step-up]
    G -. missing .-> K[Durable account-wide clear job]
    H -. not delivered .-> L[Server-owned export artifact]
```

## Proven observations

### Hydration clearly separates current Iris state from a cached projection

- `ProfileMemoryControls` starts from an honestly empty production state, then
  requests Iris review, consent, and pause data. A successful non-empty review
  maps server records into the control projection; the component does not ship
  fabricated customer memories as initial facts.
- The browser persists its projection under the exact key
  `oshun-profile-memory-controls-v1`. When review is unavailable, retained
  cached rows remain readable beside an explicit unavailable state and retry.
  When Iris returns no records, the rendered outcome still depends on the local
  projection and pause response rather than proving that every cached row was
  authoritatively erased.
- Category and scope chips, fact detail, provenance, recall history, downstream
  counts, and the local audit-chain link operate on the hydrated browser model.
  They are useful inspection affordances, but selecting a row does not issue a
  second server read.

### Edit and conflict resolution are deliberately browser-local

- `handleEditSubmit` compares the editor's expected revision against browser
  state, calls the local edit reducer, writes the next projection to
  localStorage, and emits redacted browser telemetry. It does not send a BFF
  edit request even when the selected row originated in Iris.
- The edit reducer supersedes the displayed item and creates a child revision
  with `editedFromItemId`. The resulting audit chain describes operations in
  this browser model; it is not a durable server ledger for the Iris record.
- The component listens for the same storage key's `storage` event. Two tabs on
  one origin can therefore expose a stale-draft conflict and choose the remote
  localStorage revision or layer another local revision on it. Two devices do
  not share that CAS boundary.

### Pause and single-record deletion reach durable Iris authority

- Signed-in pause toggles call the Iris adapter and commit the returned state
  only after success. The offline case fails closed: the UI does not pretend a
  pause was accepted when the mutation failed.
- The real-BFF suite proves the behavioral gate, not just the toggle. Continuity
  reports memory `off`; a live remember attempt returns
  `409 iris_memory_paused`; the blocked sentence is absent from review; resume
  persists `paused: false`; and a later remember call creates a visible row.
- Two isolated browser contexts with the same BFF identity read the same
  account-scoped pause after navigation/reload. That is durable read-back, not
  a claim of socket-driven synchronization between already-mounted pages.
- Deleting a synced fact sends hard mode, a reason, and `confirmed=true` to the
  management route. The durable Iris adapter tombstones the id, and a later
  review hides it. Only after a successful response does the component mirror
  the deletion into its local reducer.

### Full clear and export combine unlike authorities in one surface

- Typing `FORGET EVERYTHING` plus `iris-step-up` can create or cancel a
  seven-day grace object in browser state. The library tests the state machine,
  and the profile spec tests its banner and telemetry, but there is no matching
  Iris BFF full-clear route or scheduled worker consuming that record.
- For synced state, export first posts to the Iris adapter and displays a
  non-secret receipt with id, count, and checksum metadata. The downloadable
  “signed” bundle is then assembled from the browser control projection by
  `exportIrisUserMemoryBundle`; the server does not return that artifact's
  bytes through this flow.
- `profile-memory-controls.spec.ts` contains four local/stub-driven cases.
  `memory-edit-pause-forget.spec.ts` contains sixteen cases spanning anonymous
  gating, failure/retry, local conflicts, real Iris hydrate/export/delete,
  pause/read-back, two contexts, and the blocked-write boundary.

## Boundaries and gaps

- **Cached rows are not a fresh Iris read.** Unavailable and retry states must
  stay visible whenever the page is showing retained browser data.
- **A local revision is not a server edit.** The UI cannot promise that its
  revised content changed the durable Iris fact.
- **A same-origin conflict is not cross-device compare-and-swap.** The storage
  event model coordinates tabs sharing one browser origin, not independent
  devices or BFF writers.
- **A static phrase is not server-issued step-up.** `iris-step-up` never reaches
  the route as a credential, and `confirmed` currently defaults to true when
  absent at the server boundary.
- **A local grace record is not a deletion schedule.** Nothing on this page
  registers, executes, or proves an account-wide full-memory clear.
- **A tombstone mirror is not a second deletion.** The server tombstone is
  authoritative for a synced id; the local reducer updates the projection only
  after that mutation succeeds.
- **An export receipt is not the downloaded bundle.** Server metadata and the
  browser-composed artifact have distinct provenance and assurance levels.
- **A memory export is not a DSAR lifecycle.** The separate data-rights journey
  owns request, status, cancellation, and delivery claims.
- **Historical `/v1/memory` smoke is not current Iris proof.** The present flow
  uses `/v1/iris/adapter/*`; the 2026-05-29 walker cannot substantiate later
  route behavior.
- **Source reconciliation is not execution.** Neither cited application suite
  was rerun for this documentation audit.

## Re-run evidence

Run the two focused suites sequentially against one production-like web/BFF
pair and one immutable commit:

```bash
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/profile-memory-controls.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/memory-edit-pause-forget.spec.ts --project=chromium --workers=1
```

Retain the commit, browser and BFF reports, backing-store tier, seeded identity,
and before/after Iris snapshots. A stronger follow-up should also retain a
restart result, two real-device traces, the server-issued step-up exchange, and
a durable full-clear request through execution and receipt.

## Source trail

- [Current journey contract](../journeys/memory-edit-pause-forget.md)
- [Current coverage registry](../journeys/coverage.md)
- [Profile memory controls](../../apps/oshun/web/src/app/profile/memory/ProfileMemoryControls.tsx)
- [Profile memory state bridge](../../apps/oshun/web/src/app/profile/memory/memory-state.ts)
- [Browser control reducers](../../libs/oshun/memory-iris/src/user-memory-controls.ts)
- [Iris adapter route](../../apps/oshun/bff/src/routes/iris.ts)
- [Iris adapter and durable store](../../apps/oshun/bff/src/adapters/iris-memory-adapters.ts)
- [Durable Iris startup wiring](../../apps/oshun/bff/src/server.ts)
- [Local control spec](../../apps/oshun/web/e2e/profile-memory-controls.spec.ts)
- [Real-BFF memory spec](../../apps/oshun/web/e2e/memory-edit-pause-forget.spec.ts)

## Cross-references

- [Profile memory view evidence](../customer/09-account/profile-memory.md)
- [Account deletion and DSAR journey](../journeys/account-deletion-and-dsar.md)
- [Account deletion and DSAR result](./account-deletion-and-dsar.md)
- [Assistant handoff result](./assistant-handoff-context-carry.md)
- [Crisis-aware tone-policy result](./crisis-aware-tone-policy.md)
- [Current Docs Center coverage audit](../../DOCS_CENTER_PAGE_AUDIT_2026-07-16.md)

## Open questions

- Which server edit contract will accept revision preconditions and return a
  durable conflict that independent devices can resolve safely?
- Which authentication flow will issue, expire, bind, and consume a genuine
  step-up credential for destructive memory operations?
- Where will full-clear request, cancellation, grace expiry, execution, and
  completion receipt live as one durable account-scoped lifecycle?
- Should an authoritative empty Iris review clear cached rows, quarantine them,
  or preserve them under an explicitly historical/offline label?
- Will export return a server-owned artifact with durable retention and access
  controls, or remain separate from the account DSAR workflow?
- When will “forget all mentions” across authorized actors gain a real UI,
  policy contract, and multi-record server operation?
