---
path: /[surface]
surface: telegram-miniapp
domain: discovery
auth: none
source: apps/oshun/telegram-miniapp/src/app/[surface]/page.tsx
status: drafted
last_walked: '—'
---

# Telegram Mini App · Per-surface detail

## Purpose

The dynamic per-surface view renders one of seven curated Oshun surfaces inside
Telegram. The route param `surface` is validated against `SURFACE_SLUGS`; an
unknown slug falls back to `'today'` (the Tara ritual). All page content comes
from the module-level `surfaceContent` map — the page itself does no fetch and
reads no session.

Source: `apps/oshun/telegram-miniapp/src/app/[surface]/page.tsx` (server
component) which renders the client component
`apps/oshun/telegram-miniapp/src/app/[surface]/SurfaceWorkspace.tsx`. The seven
slugs are statically generated via `generateStaticParams()`.

## Entry points

- From `/` (home) via a `surfaceLink` anchor — href `/${slug}/`
- Direct URL — `/today/`, `/sophia/`, `/veritas/`, `/nyx/`, `/arete/`,
  `/nisaba/`, `/illustration/` (all pre-rendered by `generateStaticParams`)
- Any other `/[surface]/` value renders the Tara ritual (`'today'`) fallback
  rather than a 404 (`isSurfaceSlug` guard in `page.tsx`)

## Layout regions

`<main className="miniShell surfaceShell">` with `--surface-accent` set from
`surface.accent`:

- **Header** — `<a href="/" className="backLink">Back</a>` (above the header
  block), then `<header className="surfaceHeader">`: eyebrow
  (`surface.eyebrow`), h1 (`surface.title`), deck paragraph (`surface.deck`),
  and a `<div className="metaStrip">` showing three spans: `surface.domain`,
  `Telegram Mini App`, `Curated only`.
- **Main** — `<div className="surfaceGrid">` containing
  `<section className="workspacePanel" aria-label="${surface.title} workspace">`,
  whose body is dispatched by `renderSurfacePayload(surface, setStatus)`
  per-slug (see Interactions).
- **Aside / secondary panel** —
  `<aside className="inspectorPanel" aria-label="Evidence and provenance">`: the
  `EvidenceInspector` (Evidence tablist + citation drill-down), a
  `provenanceBlock` (h2 "Provenance" + list of `surface.provenance`), and a
  `disclosure` paragraph (`surface.disclosure`).
- **Footer / sticky bottom** —
  `<div className="actionBar" aria-label="Available actions">` with three
  buttons (primary action, secondary action, View provenance), followed by
  `<p className="statusLine" role="status">` echoing the current `status`
  (initial value `Ready`).

## States

- [ ] **Loading** — N/A: content is read synchronously from `surfaceContent`; no
      async fetch, no loading state.
- [ ] **Empty** — N/A: every slug maps to a fully populated `surfaceContent`
      entry; evidence/provenance arrays are non-empty in data.
- [ ] **Populated (short)** — the only state: the per-slug payload, evidence
      tabs (2–3 items), and provenance list (3 items) all render from data.
- [ ] **Populated (long)** — N/A: per-surface content is fixed-size in
      `surface-data.ts`; no list grows unbounded.
- [ ] **Error (recoverable)** — N/A: no query to fail.
- [ ] **Error (unrecoverable)** — N/A: unknown slugs fall back to `'today'`
      rather than erroring; no server data dependency to 500.
- [ ] **Offline** — page renders from bundled data once cached; action buttons
      only mutate local `status` text (no network), so they work offline; no SW
      configured (see open questions).
- [ ] **Gated** — N/A: no auth/role/flag gate; reads no session or `initData`.
- [ ] **Standalone PWA** — N/A: hosted inside the Telegram WebApp container; no
      manifest/SW in the app (see open questions).
- [ ] **Slug fallback** — any non-`SURFACE_SLUGS` param renders the Tara ritual
      (`'today'`) surface (verify via e.g. `/unknown/`).

## Interactions

Shared chrome (present on every surface):

- [ ] **Back** (link)
  - Function: navigates to `/` (home surface list).
  - Keyboard: native anchor; Tab-reachable; Enter activates.
  - Screen reader: announces "Back, link".
  - Touch target: per `.backLink` CSS; not asserted in code.
  - Mobile (≤ 640 px): no code-level delta.
  - Disabled when: never.
  - Offline behavior: standard navigation.
  - Telemetry: _None._

- [ ] **Evidence tab** (button, `role="tab"`, one per `surface.evidence` item)
  - Function: sets `activeEvidenceId`; the citation drill-down below updates to
    that item's `source`, `excerpt`, and `Open citation` link.
  - Keyboard: button in a `role="tablist"`; Tab/Enter/Space activate.
  - Screen reader: announces the evidence `label`; selected tab carries
    `aria-selected`.
  - Disabled when: never.
  - Offline behavior: local state only; no network.
  - Telemetry: _None._

- [ ] **Open citation** (link, inside the active citation drill-down)
  - Function: anchor to `activeEvidence.href` (e.g.
    `/nisaba/passages/attention-context`) — an Oshun-web deep path, not a Mini
    App route.
  - Disabled when: never.
  - Telemetry: _None._

- [ ] **Primary action** (button, `actionBar`, label = `surface.primaryAction`)
  - Function: sets `status` to `${surface.primaryAction} queued`. No network
    write; only the `role="status"` line changes.
  - Telemetry: _None._

- [ ] **Secondary action** (button, `actionBar`, label =
      `surface.secondaryAction`)
  - Function: sets `status` to `${surface.secondaryAction} ready`.
  - Telemetry: _None._

- [ ] **View provenance** (button, `actionBar`)
  - Function: sets `status` to `Provenance inspector open` (the provenance list
    is already always visible in the aside; this only updates status text).
  - Telemetry: _None._

Per-surface workspace body (dispatched by `renderSurfacePayload`):

- [ ] **`today` — Tara ritual** (`TaraRitual`)
  - **Next breath cue** (button): advances `cueIndex` cyclically through
    `payload.breathCues`; sets status `Breath cue advanced`.
  - **Play / Pause narration** (button): toggles `narrationPlaying`; status
    `Narration playing` / `Narration paused`. No audio element is mounted (state
    only).
  - **Complete ritual** (button): sets `complete = true`; status
    `tara.ritual.completed queued`; reveals "Reflection capture is ready."
  - Renders the timer face (`payload.durationLabel`, `payload.templateId`) and
    the `payload.steps` ordered list.

- [ ] **`sophia` — Sophia Q&A** (`SophiaQa`)
  - **Save to notebook** (button): status `${payload.notebookTarget} saved`.
  - Renders question, answer blockquote, and a fact row (`confidenceBand`,
    `evidencePackId`).

- [ ] **`veritas` — Veritas claim** (`VeritasClaim`)
  - **Regret flag** (button): sets `flagged = true`; status
    `Regret flag queued for review`; label flips to `Regret flag queued` and
    class to `dangerButton`.
  - Renders claim, confidence badge, summary, and split Sources / Counterclaims
    lists.

- [ ] **`nyx` — Nyx sky** (`NyxSky`)
  - **Layer button** (button, one per `payload.sceneLayers`,
    `segmentedControl`): sets `layerIndex`; status `${label} layer selected`;
    detail text below updates.
  - **Add to calendar** (button): status `${payload.calendarEntry} added`.
  - Renders the decorative `skyScene` (`role="img"`) and event fact row
    (`observationWindow`, `qualityBand`).

- [ ] **`arete` — Arete check-in** (`AreteCheckIn`)
  - **Tick habit / Habit ticked** (button, `aria-pressed`): toggles `habitDone`;
    status `Habit tick saved` / `Habit tick cleared`.
  - **Mood button** (button, one per `payload.moods`, `segmentedControl`): sets
    `mood`; status `${mood} mood selected`.
  - Renders the humane `recoveryState` block (`payload.streak`,
    `payload.recoveryState`).

- [ ] **`nisaba` — Nisaba reader** (`NisabaReader`)
  - **Edition button** (button, one per `payload.editions`, `segmentedControl`):
    sets `editionIndex`; status `${label} edition selected`; passage + note
    update.
  - **Lexicon term button** (button, one per `payload.lexicon`,
    `segmentedControl`): sets `termIndex`; status
    `${term} lexicon entry selected`; gloss line updates.
  - **Annotation** (textarea): `defaultValue` = `payload.annotationPrompt`;
    uncontrolled, no onChange handler in code.
  - **Save annotation** (button): status `Annotation saved to notebook`.

- [ ] **`illustration` — Illustration card** (`IllustrationCard`)
  - **Request generation / Generation queued** (button): sets
    `requested = true`; status `Generation request queued with lineage`; label
    flips.
  - Renders decorative `illustrationPreview` (`role="img"`), workflow class,
    request title, prompt, `policyState`, and the `payload.lineage` ordered
    list.

## Data & contracts

- **Reads**: `surfaceContent[slug]` from `../surface-data` (module constant) —
  one of seven `SurfaceContent` union members. No network read.
- **Writes**: _None over the network._ Every button updates only local React
  state (`status`, and per-surface toggles); no BFF/`/api` call exists in this
  app.
- **Realtime**: _None._
- **Caching**: statically generated per slug via `generateStaticParams()`
  (Next.js App Router); no SWR/ISR/SW-cache configured.
- **Auth/role check**: _None._ No middleware; the page reads no session and no
  Telegram `initData`.

## Cross-references

- Sibling route: `home.md` (`/` — the surface list landing)
- Server page: `apps/oshun/telegram-miniapp/src/app/[surface]/page.tsx`
- Client workspace:
  `apps/oshun/telegram-miniapp/src/app/[surface]/SurfaceWorkspace.tsx`
- Surface data + per-slug payload types:
  `apps/oshun/telegram-miniapp/src/app/surface-data.ts`
- E2E coverage: `apps/oshun/telegram-miniapp/e2e/miniapp.spec.ts` (per-surface
  tests for sophia, today, veritas, nyx, arete, plus the landing list)
- Unused Telegram bridge helper:
  `apps/oshun/telegram-miniapp/src/lib/telegram-webapp.ts`

## Open questions / known gaps

- [ ] All action buttons are status-text-only stubs at the app level — they
      mutate local React state but issue no BFF write (no
      `tara.ritual.completed` emit, no notebook save, no calendar add, no
      regret-flag submission, no generation request). The data layer the labels
      imply (notebook/calendar/ review/generation) is not reachable from this
      Mini App in code. Confirm whether real BFF wiring is planned and which
      contracts/routes back each action.
- [ ] The Telegram WebApp runtime is NOT wired here either: no
      `telegram-web-app.js`, no `window.Telegram`, and no use of
      `src/lib/telegram-webapp.ts` (theme params, `MainButton`/`BackButton`,
      `HapticFeedback`, `initData` header). The `actionBar` buttons could be
      promoted to the Telegram `MainButton`, and the `Back` link to
      `BackButton`, but neither is done in code. Confirm intended wiring.
- [ ] The Nisaba **Annotation** textarea is uncontrolled (`defaultValue`, no
      `onChange`), so edits are never read by **Save annotation** (which emits a
      fixed status string). Confirm whether annotation capture is intended.
- [ ] No telemetry is emitted on any interaction; confirm event names if
      surface-open / action analytics are required.
- [ ] No PWA manifest/SW, so Offline / Standalone-PWA states cannot be produced
      here; confirm this is intentional for a Telegram-hosted Mini App.
