---
path: /domains/veritas/topics
surface: customer
domain: veritas
auth: signed-in
source: apps/oshun/web/src/app/domains/veritas/topics/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'
---

# Topic Hub Workspace · Veritas (default topic)

## Purpose

The Veritas Topic Hub workspace mounted with the default topic (renewable
energy, since `topicId` is unset). Functionally identical to the dynamic
`/domains/veritas/topics/[topicId]` route — same `VeritasTopicHubWorkspace`,
same rendering — but the default topic surface serves as the topics index /
showcase entry. The workspace tracks topic follow-state via the Veritas store
and supports follow alert-frequency changes.

## Entry points

- **`/domains/veritas`** index — verify the internal-namespace tool grid
- **Linked from `/explore`** — topic cards in discovery (verify)
- **Linked from `/domains/veritas/story/[id]`** — story workspace topic-hub href
  targets the topic by slug
- **Direct URL / bookmark** — yes (auth required)

## Layout regions

`page.tsx` is a `'use client'` component:

```tsx
<VeritasTopicHubWorkspace onClose={() => router.back()} origin="explore" />
```

(no `topicId` is passed, so the workspace selects the default topic via
`getTopicKey(undefined)` → `'renewable-energy'`.)

`VeritasTopicHubWorkspace` renders inline (not an overlay). Layout (anchored to
`data-veritas-topic-*` attributes):

- **Top toolbar** (line 541): back button (`onClose`), trend label and metric
  pills (`{trendLabel}`, `{tracked claims}`, `{source institutions}`), and a
  follow toggle (`data-veritas-topic-follow-toggle`,
  `data-veritas-topic-follow-state="following|unfollowed"`)
- **Hero** (line 596, `data-veritas-topic-hub-title`): `topic.eyebrow`,
  `topic.title` (clamp), `topic.headline` lede, `topic.overviewSignals` pills
- **Two-column grid** (`minmax(0, 1.8fr) minmax(300px, 0.95fr)`):
  - **Main column** (left):
    - "Current read" — metrics grid (`topic.metrics[]`: value + label)
    - "Timeline" (`data-veritas-topic-hub-timeline`): per `topic.timeline[]`, an
      article (`data-veritas-topic-timeline-entry`) colour-coded by `tone`
      (anchor / watch / other)
    - "Story clusters" — per `topic.clusters[]`, an action card
      (`data-veritas-topic-cluster-action`)
    - "Claim posture" — per `topic.claims[]`, a card with `Inspect claim` button
      (`data-veritas-topic-inspect-claim`) and a "Open claim detail" link
      (`data-veritas-topic-claim-action` → builds an href to the claim route)
    - "Sources" — per `topic.sources[]`, a card with `Inspect source` button
      (`data-veritas-topic-inspect-source`) and an "Open source lane" link
      (`data-veritas-topic-source-action`)
  - **Aside column** (right):
    - "Alert controls" (`data-veritas-topic-alert-controls`): per frequency
      (`realtime | daily | weekly`), buttons
      (`data-veritas-topic-alert-frequency`); only enabled when followed
    - "Topic follow-up" Links to related views
      (`data-veritas-topic-followup-action="topics|clusters|research"`)
- **`VeritasInspectionOverlay`** — opened by inspect buttons

## States

- [ ] **Loading** — synchronous from imported `TOPIC_HUBS`; no fetch
- [ ] **Populated** — default topic always renders fully
- [ ] **Followed** — `topicFollowed = true`; toggle button reads "Following
      topic" (accent border + accent text); alert controls become active
- [ ] **Unfollowed** — `topicFollowed = false`; toggle button reads "Follow
      topic" (muted); alert controls render but
      `handleTopicAlertFrequencyChange` no-ops when not followed
- [ ] **Alert frequency: realtime / daily / weekly** — driven by
      `followedTopic?.alertFrequency` from the Veritas store (default `'daily'`)
- [ ] **Inspection open** — `activeInspection !== null` shows the overlay
      (`handleTopicClaimInspection`, `handleTopicSourceInspection`,
      `handleTopicSophiaBundleInspection`)
- [ ] **No timeline entries / clusters / claims / sources** — verify graceful
      empty branches (the workspace currently maps unconditionally)
- [ ] **Offline** — uses imported data + local Veritas store; the follow toggle
      is local-only (verify whether it should sync to a BFF)
- [ ] **Standalone PWA** — verify the 1.8fr/0.95fr grid collapses cleanly
- [ ] **Reduced motion** — verify (no explicit animations in observed code)

## Interactions

### Top toolbar

- [ ] **Back** (button with ChevronLeft icon) — `onClose()` → `router.back()`
- [ ] **Follow toggle** (button, `data-veritas-topic-follow-toggle`,
      `data-veritas-topic-follow-state`)
  - Function: `handleTopicFollowToggle` calls `followTopic` or `unfollowTopic`
    on the Veritas store and fires `trackVeritasTopicFollowed` /
    `trackVeritasTopicUnfollowed` with `{ topicId, topicTitle, source: origin }`

### Claim cards

- [ ] **Inspect claim** (button, `data-veritas-topic-inspect-claim={claimId}`)
  - Function: `handleTopicClaimInspection(claimId)` opens overlay with the claim
    model and fires
    `trackVeritasInspectionOpened({ workspace: 'topic-hub', kind: 'evidence', itemId, source: origin })`
- [ ] **Open claim dossier** (link, `data-veritas-topic-claim-action={claimId}`)
  - Function: navigates to
    `buildDomainShellHref('veritas', claim.href, origin, stack)` → resolves to
    `/domains/veritas/claims/<claimId>`

### Source cards

- [ ] **Inspect source** (button,
      `data-veritas-topic-inspect-source={sourceId}`)
  - Function: `handleTopicSourceInspection(sourceId)` opens overlay with source
    model and fires
    `trackVeritasInspectionOpened({ workspace: 'topic-hub', kind: 'source', itemId, source: origin })`
- [ ] **Open source lane** (link, `data-veritas-topic-source-action={sourceId}`)
  - Function: navigates to source's `href`

### Sophia bundle (if present per topic)

- [ ] **Inspect bundle** — `handleTopicSophiaBundleInspection(claimId)` opens
      overlay with the Sophia-backed `pack` (citations, source graph, grounding
      status, claim checks)

### Alert controls

- [ ] **Realtime / Daily / Weekly** (buttons,
      `data-veritas-topic-alert-frequency={frequency}`)
  - Function: `handleTopicAlertFrequencyChange(frequency)` calls
    `updateTopicAlert` and fires
    `trackVeritasTopicAlertFrequencyUpdated({ topicId, topicTitle, alertFrequency, source: origin })`
  - Disabled when topic is unfollowed (the handler returns early)

### Topic follow-ups

- [ ] **Topics follow-up link** (`data-veritas-topic-followup-action="topics"`)
- [ ] **Clusters follow-up link**
      (`data-veritas-topic-followup-action="clusters"`)
- [ ] **Research follow-up link**
      (`data-veritas-topic-followup-action="research"`)

## Data & contracts

- **Reads (imported)**: `TOPIC_HUBS[topicKey]` — `getTopicKey(topicId)` returns
  `TopicKey | null`: a known id resolves to its key, an **undefined** id (this
  route — no `topicId` is passed) defaults to `'renewable-energy'`, and an
  **unknown** id returns `null` → the workspace renders
  `VeritasTopicNotFoundPanel`. This route doesn't pass `topicId`, so
  `renewable-energy` is loaded.
- **Reads (store)**: `useVeritasStore` via `useSyncExternalStore` —
  `followedTopics`, `followTopic`, `unfollowTopic`, `updateTopicAlert`
- **Reads (Sophia)**: `VERITAS_TOPIC_SOPHIA_EVIDENCE_BUNDLES[topicId]`
- **Telemetry**: `trackVeritasInspectionOpened`, `trackVeritasTopicFollowed`,
  `trackVeritasTopicUnfollowed`, `trackVeritasTopicAlertFrequencyUpdated`
- **Writes (store)**: `followTopic`, `unfollowTopic`, `updateTopicAlert`
- **Realtime**: none in this view (alert frequencies are read; actual delivery
  happens elsewhere)
- **Caching**: imported canonical data
- **Auth/role check**: shell middleware (verify)
- **Page metadata**: not set (client component)

## Cross-references

- Component:
  `apps/oshun/web/src/components/domains/veritas/VeritasTopicHubWorkspace.tsx`
- Dynamic counterpart:
  [`domains-veritas-topics-topicId.md`](./domains-veritas-topics-topicId.md)
- Claim detail handoff:
  [`domains-veritas-claims-claimId.md`](./domains-veritas-claims-claimId.md)
- Story workspaces: [`domains-veritas-story.md`](./domains-veritas-story.md),
  [`domains-veritas-story-id.md`](./domains-veritas-story-id.md)
- Customer-facing alternate: [`veritas-topic.md`](./veritas-topic.md)
- Feature spec: [`V1/features.md`](../../../V1/features.md#veritas)

## Open questions / known gaps

- [ ] No `topicId` is passed by the page, so the route is effectively pinned to
      renewable energy; confirm whether this default route should redirect to a
      slug-shaped URL once V1 wires real data
- [ ] Follow state lives in `useVeritasStore` (local store) — verify whether V1
      wants this synced to a BFF subscription
- [ ] No `sr-only <h1>` in `page.tsx`; the workspace hero `<h1>` provides the
      page title
- [ ] Alert frequencies surface as buttons but their disabled-when-unfollowed
      state may not be visually distinct — verify the disabled styling
- [ ] No tests are co-located in this folder (workspace tests live in
      `components/domains/veritas/__tests__/`)
