Journey at a glance
ActorsRoutine signed-in customerPrivacy-conscious customerMulti-device customer
Systems exercised
profile-memory.mdprofile.mdAutomation2 E2E spec(s) · verdict: partial· 2026-05-29
Journey flow#
Generated from the authored steps below — click a node to jump to that section.
flowchart TD
s1["1. Open and identify the data source"]
s2["2. Review and filter the projection"]
s3["3. Edit a displayed fact"]
s4["4. Resolve a same-origin edit conflict"]
s5["5. Pause durable memory writes"]
s6["6. Verify the pause from another browser cont…"]
s7["7. Forget one synced fact"]
s8["8. Request and cancel a full clear"]
s9["9. Export the visible memory state"]
s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9
click s1 href "#1-open-and-identify-the-data-source"
click s2 href "#2-review-and-filter-the-projection"
click s3 href "#3-edit-a-displayed-fact"
click s4 href "#4-resolve-a-same-origin-edit-conflict"
click s5 href "#5-pause-durable-memory-writes"
click s6 href "#6-verify-the-pause-from-another-browser-context"
click s7 href "#7-forget-one-synced-fact"
click s8 href "#8-request-and-cancel-a-full-clear"
click s9 href "#9-export-the-visible-memory-state"
s9 --> v(["verdict: partial"])
click v href "../results/memory-edit-pause-forget.html"
style v stroke:#d6a531,color:#d6a531
This journey separates the four persistence levels currently present on
/profile/memory: BFF-hydrated Iris records, a server-authoritative pause and
single-record delete, browser-local edits and audit projections, and a
browser-local seven-day full-clear request. The UI exposes all four in one
surface, so the distinction is essential to an honest walkthrough.
Personas#
- Routine signed-in customer — reviews what Iris remembers, corrects a displayed fact, and pauses future durable writes.
- Privacy-conscious customer — deletes one synced memory and needs to know which controls are server-backed versus local simulations.
- Multi-device customer — expects the server-backed pause to follow the account to another browser context.
Pre-conditions#
- The customer is signed in; anonymous
/profile/memoryvisits are redirected to/welcome?redirect=/profile/memory. ProfileMemoryControlscan hydrate fromGET /v1/iris/adapter/review?role=settingsand read pause state from the Iris adapter.oshun-profile-memory-controls-v1may contain a cached local projection. The production initial state is empty; hydration or an existing cache supplies rows. An unavailable read can leave cached content visible with explicit status copy, while an empty response is interpreted alongside that local projection and the pause response.- The phrase
iris-step-upis a client challenge used by this component. It is not a primary-credential or server-issued step-up token.
Steps#
1. Open and identify the data source#
- Open
/profile/memoryand wait for the Iris hydration status. - A successful non-empty review shows server-derived facts. An empty review shows the explicit no-records state when no cached seed is present.
- If the review request fails,
data-profile-memory-bff-statuskeeps cached controls readable and offers a retry. Cached rows are not evidence that the BFF currently holds those facts.
2. Review and filter the projection#
- Category and scope chips filter the local view model.
- Selecting a fact opens its body, origin, provenance chain, recall history, downstream reference count, and audit-chain link.
- These fields are a browser projection of the currently hydrated control state; the detail pane performs no additional authoritative read.
3. Edit a displayed fact#
- Change the label and content, then submit.
detectIrisUserMemoryEditConflictcompares the expected revision count with the browser state. A clean edit callseditIrisUserMemoryFact, supersedes the selected item, and creates a child revision witheditedFromItemId.- The edit is persisted to localStorage and browser telemetry only. There is no
BFF edit request in
handleEditSubmit, even when the original fact came from Iris.
4. Resolve a same-origin edit conflict#
- A second tab can update the same localStorage record while the first tab holds a stale draft.
- Keep remote version selects the replacement already visible in browser state and rebases the editor.
- Save my edit as the next revision creates another local revision on top of that replacement.
- This is a same-origin browser conflict model, not a server or cross-device compare-and-swap contract.
5. Pause durable memory writes#
- Toggle off-the-record mode.
- For a signed-in customer, the component sends
PATCH /v1/iris/adapter/pause?role=settingsand commits the returned pause only when that request succeeds. - Iris continuity reports
indicator: "off"; live remember attempts return409 iris_memory_paused; the assistant dock exposes memory scopeoff. - Resume from the banner or header toggle to persist
paused: falsethrough the same route.
6. Verify the pause from another browser context#
- Open a second isolated context with the same BFF identity.
- Its profile and assistant dock read the server pause as
off. - Resume on the first context, reload the second, and verify the assistant returns to session memory.
- This proves account-scoped pause propagation through the shared BFF; it does not prove real-time push between already-open devices.
7. Forget one synced fact#
- Select a fact, type the component challenge, and choose permanent delete.
- For a fact from a successful Iris hydration, the component first calls
DELETE /v1/iris/adapter/memory/:memoryId?role=settings&mode=hard. - A successful response is mirrored into the local reducer as a tombstone; subsequent Iris review excludes the deleted id and content.
- The browser challenge gates the button path, but the delete route itself does not receive or validate that local challenge as server-issued step-up proof.
8. Request and cancel a full clear#
- Type
FORGET EVERYTHINGand the local step-up phrase. requestIrisUserMemoryFullClearrecords a seven-day grace object inside the local control state; the component persists it to localStorage and shows the grace banner.- Cancel clear updates that same local record and emits browser telemetry.
- No BFF request, server job, or durable account-wide schedule is created by these controls.
9. Export the visible memory state#
- When the page has a synced Iris record, export first requests
/v1/iris/adapter/export?role=settingsand shows the non-secret server receipt. - The downloadable signed bundle is still composed from the browser control state and includes its local audit chain.
- A local-only export must not be described as a server DSAR receipt.
Post-conditions#
- A successful pause/resume round trip is reflected by the server-authoritative Iris pause state and assistant memory-scope marker.
- A successfully deleted synced fact is absent from the next Iris review.
- Edits, conflict resolutions, tombstone mirrors, and the full-clear grace object remain in the browser control state.
- No server-authoritative full-memory-clear job exists from this journey.
- Browser telemetry contains metadata such as category, scope, revision, and outcome, but not raw edited memory content.
Failure modes#
- Anonymous access — redirects before the memory surface mounts.
- Iris review unavailable — cached rows remain visible with an unavailable banner; they must not be presented as fresh server state.
- Offline pause — the signed-in mutation fails closed, the toggle returns to false, and no pause banner appears.
- Wrong local challenge — the component performs no delete or full-clear mutation.
- BFF delete fails — the local tombstone is not committed for a synced fact.
- Tenant-to-consumer scope promotion — the local scope reducer rejects it and preserves the tenant badge.
- Full-clear grace expires — nothing executes automatically in this component; server cron execution is not wired to the local record.
- Two real devices edit the same fact — the localStorage conflict model cannot reconcile them.
E2E coverage#
apps/oshun/web/e2e/profile-memory-controls.spec.tscovers local edit, export, tombstone, full-clear request/cancel, pause banner, telemetry redaction, and localStorage behavior.apps/oshun/web/e2e/memory-edit-pause-forget.spec.tscovers same-origin conflict resolution; real Iris hydrate/export/delete; server-authoritative pause, blocked remember, review read-back, assistant marker, two-context propagation; route gating; offline pause failure; retry; and tenant-scope rejection.- The scheduled full-clear execution remains deliberately uncovered because no server schedule is created by the page.
Per-view files touched#
customer/09-account/profile-memory.md— primary controls.customer/09-account/profile.md— entry route and assistant-dock read-back.
Cross-references#
apps/oshun/web/src/app/profile/memory/ProfileMemoryControls.tsx— component persistence and BFF boundaries.libs/oshun/memory-iris/src/user-memory-controls.ts— local control reducers, audit chain, and grace-window model.account-deletion-and-dsar.md— separate BFF data-rights request lifecycle.crisis-aware-tone-policy.md— crisis suppression boundaries.
Open questions#
- Add a server edit contract with revision preconditions and cross-device conflict semantics.
- Replace the static browser phrase with a genuine server-issued step-up proof for destructive operations.
- Connect request/cancel full clear to an account-scoped durable job, scheduled execution, and receipt.
- Define whether an empty authoritative Iris review should discard all cached seed rows rather than leave them visible.
- Add the promised multi-actor “forget all mentions” surface.