---
path: /domains/arete/journal
surface: customer
domain: arete
auth: signed-in
source: apps/oshun/web/src/app/domains/arete/journal/page.tsx
status: walked
last_walked:
  '2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data
  (2xx), console/page-errors, expected content, screenshot verified; live
  screen-reader, touch, offline, and telemetry-delivery checks pending a manual
  AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content
  re-verified 2026-06-03 against current source'
---

# Arete Journal Reflection Workspace

## Purpose

Internal `/domains/arete/*` journal-and-reflection workspace. A three-panel
layout: reflection prompts (evening / weekly / open windows) · compose draft ·
continuity inspector (recent reflections, brief, theme-graph links). Wired to
the `@oshun/domain-arete` library for canonical reflection builders, the Oshun
assistant entry, and a domain-route helper for navigating across Nisaba /
Veritas / Tara / Arete / Nyx.

## Entry points

- **Direct URL / bookmark** — yes (signed-in)
- **`?entry=<id>` query parameter** — `useBrowserSearchParams().get('entry')` is
  read by the page and passed as `initialEntryId`; selects a specific entry on
  mount
- **Back navigation** — page passes `onBack={() => router.back()}`
- **Theme-graph link from another reflection** — the workspace itself can
  produce a `targetPath` of the form `?entry=<id>`; navigating to this page with
  that param sets `selectedEntryId`
- **Polished `/arete` Journal tab** — the polished room's `LSubNav` item carries
  `href="/domains/arete/journal"` and navigates here

## Layout regions

`page.tsx` is a `'use client'` page using `useRouter` and
`useBrowserSearchParams`. Renders
`<AreteJournalReflectionWorkspace onBack={() => router.back()} initialEntryId={searchParams.get('entry')} />`.
Component at
`apps/oshun/web/src/components/domains/arete/AreteJournalReflectionWorkspace.tsx`.

- **Container (section)**: cream-paper radial gradient background; column flex;
  padding 24px (desktop) / 16px
- **Header tile**: Back button (`aria-label="Go back"`), eyebrow + h1 "Keep
  reflection close enough to shape the next move.", lede paragraph, "Open
  reflection assistant" CTA (calls `dispatchOshunAssistantOpen`)
- **Window selector**: row of 3 pill buttons — `evening`, `weekly`, `open`
  (`data-arete-journal-reflection-period`)
- **Metric strip** (3 tiles): Entries this week / Reflection tone / Draft state
  (Captured / In progress)
- **Artifact action panel**: `<AreteArtifactActionPanel />` with save and export
  handlers, saved-at timestamp from local artifact store
- **Three-column grid (desktop)** / single column mobile:
  - Left: **Reflection prompts** lane —
    `data-arete-journal-reflection-prompt-lane`. Eyebrow "Reflection mode",
    reflection-window heading and summary, list of available prompts (filtered
    by `reflectionWindowPromptCategories[window]`). Each is a button with
    category eyebrow + text.
  - Middle: **Compose** surface — `data-arete-journal-reflection-compose`.
    Selected-prompt title, instruction body, `textarea` draft
    (`aria-label="Journal reflection draft"`), draft-state label (Captured / In
    progress), two CTAs: "Capture reflection" and "Send to assistant"
    (`dispatchOshunAssistantOpen` with prompt+draft+continuity context)
  - Right: **Reflection continuity inspector** —
    `data-arete-journal-reflection-inspector`. "Recent reflections" section with
    entry-select buttons. Continuity brief card (`MessageSquareQuote` icon) with
    reflectionSummary text and 2 talking points. "Tags in focus" chips.
    **Theme-graph links**: per `themeGraphThreads`, an article with theme label,
    summary, source signals, and per-link buttons that dispatch
    `handleThemeGraphLink`.

## States

- [ ] **Loading** — N/A: client component with sync fixture; saved-at hydrated
      via `useEffect` against localStorage-backed `readSavedAreteArtifact`
- [x] **Initial entry from `?entry=<id>`** — if the param matches an
      `entries[i].entry.entryId`, that entry is pre-selected
- [x] **Window switched** — toggling evening/weekly/open re-derives
      `availablePrompts` and resets `selectedPromptId` to a category-matching
      prompt
- [x] **Draft captured** — `setDraftCaptured(true)` updates the
      `data-arete-journal-reflection-draft-state` attribute to `"captured"`
- [x] **Theme-graph link to a practice** — when
      `link.targetKind === 'practice'`, the handler parses the `?entry=<id>`
      from `link.targetPath`, sets `selectedEntryId`, and forces window to
      `'open'`
- [x] **Theme-graph link to a non-practice surface** —
      `router.push(buildConceptGraphDomainHref(link))` navigates to Nisaba /
      Veritas / Tara / Arete / Nyx based on `link.targetKind`
- [x] **Desktop vs. mobile layout** — `useSemanticViewport` +
      `isLaptopUp(viewport)` gates the 3-column grid vs. single-column
- [x] **Saved-at hydrated** — `setArtifactSavedAt` reads localStorage on mount
      and on `artifactId` change
- [ ] **Error (recoverable)** — relies on app-level error boundary
- [ ] **Offline** — purely client; identical offline (saved artifacts live in
      localStorage)
- [x] **Shell entitlement granted** — `/domains/arete?path=/journal` is Arete
      pro-gated; E2E upgrades the seeded account through the real admin
      bulk-entitlements BFF flow before hydration
- [ ] **Free-tier gated fallback** — verify the shell upgrade screen for users
      without Arete entitlement
- [ ] **Standalone PWA** — no shell chrome around the page

## Interactions

### Top bar

- [x] **Back button** (`aria-label="Go back"`)
  - Function: calls `router.back()` via `onBack` prop
- [x] **"Open reflection assistant"** (button)
  - Function: dispatches `dispatchOshunAssistantOpen` with source
    `arete-journal-reflection:<window>` and a prompt that includes the window
    label, the selected prompt text, and the selected entry's excerpt
  - Telemetry: relies on the assistant-entry event for instrumentation

### Window selector

- [x] **Evening / Weekly / Open** (3 pill buttons)
  - Function: `setWindow(option)` + reset `selectedPromptId` to a prompt
    matching the new window's categories
  - Data attribute: `data-arete-journal-reflection-period={option}`
  - Touch target: 10px+14px padding; E2E verifies ≥ 44 px height on mobile

### Reflection prompts (per `availablePrompts`)

- [x] **Prompt card** (button,
      `data-arete-journal-reflection-prompt={prompt.promptId}`,
      `data-arete-journal-reflection-prompt-selected="true|false"`)
  - Function: `setSelectedPromptId(prompt.promptId)`
  - Active style: accent border + tinted background

### Compose surface

- [x] **Draft textarea** (`aria-label="Journal reflection draft"`,
      `data-arete-journal-reflection-draft`)
  - Function: typing updates `draftText` and resets `draftCaptured` to `false`
- [x] **"Capture reflection"** (button, `data-arete-journal-reflection-capture`)
  - Function: `setDraftCaptured(true)`
- [x] **"Send to assistant"** (button,
      `data-arete-journal-reflection-assistant-handoff`)
  - Function: dispatches `dispatchOshunAssistantOpen` with source
    `arete-journal-reflection:assistant:<promptId>` and a multi-line prompt
    (prompt + draft + continuity cue from selected entry)

### Continuity inspector

- [x] **Entry selector** (per `entries`, `data-arete-journal-reflection-entry`)
  - Function: `setSelectedEntryId(item.entry.entryId)`
  - Style: active gets accent border, includes the sentiment tone as a chip
- [x] **Theme-graph link** (per `thread.links`,
      `data-arete-journal-theme-graph-action`,
      `data-arete-journal-theme-graph-action-kind`)
  - Function: `handleThemeGraphLink(link)` — either selects an entry (when
    `link.targetKind === 'practice'`) or routes via
    `buildConceptGraphDomainHref(link)`

### Artifact actions (`AreteArtifactActionPanel`)

- [x] **Save** — `handleSaveArtifact` calls `saveAreteArtifact` with the
      snapshot JSON (window, promptId, entryId, draftText, draftCaptured)
- [x] **Export** — `handleExportArtifact` calls
      `buildAreteJournalReflectionExport` and `triggerAreteArtifactDownload`
- [x] **Share disabled state** — `AreteArtifactActionPanel` renders the
      `journal_reflection` share action disabled under the current policy

## Data & contracts

- **Reads**: imports from `@oshun/domain-arete`:
  `buildAreteJournalReflectionExport`, `buildAreteCoachingSummaryShellModel`,
  `buildCanonicalAreteCoachingSummary`,
  `buildAreteThemeConceptGraphReflections`,
  `buildAreteThemeConceptGraphThreads`, `getAreteArtifactPolicy`. Types:
  `AreteCanonicalCoachingSummary`, `AreteJournalEntry`, `AreteJournalPrompt`,
  `AreteJournalPromptCategory`, `AreteThemeConceptGraphLink`. In-file fixture
  `prompts` (5 entries) and `entries` (multiple `ReflectionEntryModel`s).
- **Writes**: `saveAreteArtifact` (localStorage); no BFF write
- **Realtime**: none
- **Caching**: client bundle + localStorage for saved artifacts
- **Auth/role check**: app-level middleware

## Cross-references

- Domain hub: [`arete.md`](./arete.md)
- Related polished surfaces: [`arete-review.md`](./arete-review.md) (weekly
  review uses similar prompt scaffolding),
  [`arete-coaching.md`](./arete-coaching.md) (letter from Lilith)
- Sibling internal alternates:
  - [`domains-arete-plan-review.md`](./domains-arete-plan-review.md)
  - [`domains-arete-progress.md`](./domains-arete-progress.md)
- Cross-domain: theme-graph links route to Nisaba / Veritas / Tara / Arete / Nyx
  via `buildConceptGraphDomainHref`
- Component source:
  `apps/oshun/web/src/components/domains/arete/AreteJournalReflectionWorkspace.tsx`
- Library: `libs/domain-arete/*` (resolves as `@oshun/domain-arete`)
- Assistant dispatch: `apps/oshun/web/src/navigation/assistant-entry.ts`
- Artifact support:
  `apps/oshun/web/src/components/domains/arete/areteArtifactSupport.ts`,
  `AreteArtifactActionPanel.tsx`

## E2E coverage

- `apps/oshun/web/e2e/arete-journal-reflection-workspace.spec.ts` covers the
  dedicated route's prompt selection, draft capture, top-bar and compose
  assistant event payloads, localStorage save, saved-at hydration after reload,
  entry selection, disabled share control, and Markdown export.
- The same spec covers shell hydration through
  `/domains/arete?origin=home&path=%2Fjournal` using the real admin
  bulk-entitlements BFF flow to grant pro access before asserting the journal
  workspace, all three reflection windows, and the shell back button.
- Theme-graph coverage includes `?entry=entry-language-fragile`, the in-place
  practice link back to the architecture reflection, and the Tara ritual route
  generated by `buildConceptGraphDomainHref`.
- Responsive coverage asserts the desktop three-column lane alignment, mobile
  stacked order, no horizontal overflow, and minimum touch targets for the
  window selector.

## Open questions / known gaps

- [ ] Direct `/domains/arete/journal` still renders as a bare internal page;
      shell chrome is covered separately through `/domains/arete?path=/journal`
- [ ] Saved artifacts live in localStorage only; no server persistence
- [ ] `dispatchOshunAssistantOpen` event payloads are verified; separately
      verify how the receiving assistant surface (Sophia / Lilith) consumes the
      seeded prompt
- [ ] Theme-graph E2E verifies the practice link and Tara ritual route; confirm
      the remaining Nisaba, Veritas, Arete, and Nyx route mappings against
      actual route slugs
- [ ] In-file `entries` and `prompts` arrays are fixtures; no BFF wiring
- [ ] How does this surface relate to the polished `/arete` "Journal" tab (the
      polished room's `LSubNav` "Journal" item carries
      `href="/domains/arete/journal"` and navigates here)
