---
path: /domains/veritas/headlines
surface: customer
domain: veritas
auth: signed-in
source: apps/oshun/web/src/app/domains/veritas/headlines/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. 2026-06-28
  Codex Headline Analyzer addendum — authenticated stories/filter/sort/keyboard
  selection/close, detail metrics, framing expansion, tracking history,
  untracking, reduced motion, standalone PWA restore, offline mobile use, and
  horizontal overflow checks are covered by the E2E evidence below.
---

# Headline Analyzer · Veritas

## Purpose

The Veritas Headline Analyzer is a full-screen workspace for comparing how
multiple sources frame the same story. It opens fixture-backed headline
comparisons, scores emotional manipulation and clickbait, shows per-source
framing/omission analysis, and lets the user keep a session-local tracked-story
history.

## Entry Points

- **Direct URL / bookmark** — `/domains/veritas/headlines` (auth required)
- **`/domains/veritas` index** — internal Veritas tool grid
- **Shell navigation history** — opening after `/library` lets the Close control
  return with `router.back()`

## Layout Regions

`page.tsx` is a client route that renders an `sr-only` `<h1>` and mounts
`<VeritasHeadlineAnalyzer onClose={() => router.back()} />`.

- **Overlay root**: `[data-veritas-headline-analyzer]` with current
  `data-veritas-headline-view`, selected story, active category/sort, total and
  filtered story counts, tracked count, and reduced-motion state.
- **Header**: `[data-veritas-headline-header]`, title, tablist
  `[data-veritas-headline-tabs]`, and `[data-veritas-headline-close]`.
- **Stories view**: `[data-veritas-headline-stories-view]` with intro copy,
  category filter buttons, sort buttons, keyboardable story cards, empty-filter
  branch, and a tracked-story chip section when any story is tracked.
- **Detail view**: `[data-veritas-headline-detail-view]` with story header,
  aggregate metrics, sorted source comparison chart, per-source headline cards,
  and expandable framing panels.
- **History view**: `[data-veritas-headline-history-view]` with empty guidance
  or tracked rows, per-story timeline entries, View, and Untrack actions.

## States

- [x] **Authenticated render** — route opens through the signed-in shell and
      renders `[data-veritas-headline-analyzer]`.
- [x] **Stories populated** — story-card count matches `SAMPLE_HEADLINES`.
- [x] **Category-filtered stories** — category buttons update the visible card
      count and active filter state.
- [x] **Sorted stories** — manipulation and clickbait sort buttons place the
      highest-scoring fixture story first.
- [x] **Detail disabled before selection** — detail tab is disabled until a
      story is selected.
- [x] **Detail populated** — selected story, aggregate metrics, source chart,
      and sorted variants render from `HeadlineComparison`.
- [x] **Framing expanded** — per-source framing panels reveal emphasis,
      omissions when present, and neutral rewrite guidance.
- [x] **History empty / tracked** — history view renders empty guidance before
      tracking, and tracked rows/timelines after tracking.
- [x] **Untracked** — untracking removes the row and returns history to its
      empty state.
- [x] **Offline** — mobile test switches the browser context offline and still
      sorts, opens detail, expands framing, tracks, and opens history from local
      data.
- [x] **Standalone PWA** — `mockStandaloneLaunch()` restores directly into the
      route from `/?surface=pwa`.
- [x] **Reduced motion** — card/chart animations collapse to `none` under
      `prefers-reduced-motion`.
- [x] **Mobile horizontal overflow** — standalone/offline test asserts no
      document overflow at 390×844.

## Interactions

### Header

- [x] **Close** — `[data-veritas-headline-close]` has accessible name "Close
      Headline Analyzer", a 44×44 target, keyboard activation, and returns to
      `/library` in the desktop path.
- [x] **Tabs** — `stories`, `detail`, and `history` expose active state through
      `data-veritas-headline-tab-active`.

### Stories View

- [x] **Category chip** — `[data-veritas-headline-category-filter="<category>"]`
      applies the category and exposes active state.
- [x] **Sort chip** —
      `[data-veritas-headline-sort="date|manipulation|clickbait"]` applies
      fixture-backed ordering and exposes active state.
- [x] **Story card** — `[data-veritas-headline-story-card="<story-id>"]` is
      keyboardable, labelled `Open <story title>`, and switches to detail.
- [x] **Tracked story chip** —
      `[data-veritas-headline-tracked-chip="<story-id>"]` reopens a tracked
      story from the stories view.

### Detail View

- [x] **Back to stories** — `[data-veritas-headline-action="back-to-stories"]`
      switches back to the stories view.
- [x] **Track toggle** — `[data-veritas-headline-track-toggle="<story-id>"]`
      adds/removes the story from session-local tracking.
- [x] **Metrics** — `average-manipulation`, `average-clickbait`, `most-neutral`,
      and `most-sensational` are exposed as data-backed metric blocks.
- [x] **Chart rows** — `[data-veritas-headline-chart-row="<source>"]` renders
      variants sorted by emotional-manipulation score.
- [x] **Framing toggle** — `[data-veritas-headline-framing-toggle="<source>"]`
      opens `[data-veritas-headline-framing-panel="<source>"]`.

### History View

- [x] **View tracked story** —
      `[data-veritas-headline-history-view-story="<story-id>"]` returns to the
      story detail view.
- [x] **Untrack** — `[data-veritas-headline-history-untrack="<story-id>"]`
      removes the tracked row.
- [x] **Timeline entries** —
      `[data-veritas-headline-history-timeline="<story-id>"]` renders one entry
      per headline variant.

## Data & Contracts

- **Reads**: `SAMPLE_HEADLINES` from
  `apps/oshun/web/src/lib/veritas/veritas-extended-simulation.ts`.
- **Types**: `HeadlineComparison` and `HeadlineVariant` from
  `veritas-extended-types.ts`.
- **Writes**: local React state only (`view`, `selectedStory`, `categoryFilter`,
  `sortBy`, `trackedStories`).
- **Realtime / external network**: none for the core route; the analyzer remains
  usable while the browser context is offline.
- **Telemetry**: no route-local telemetry import or event dispatch.
- **Auth/role check**: shell middleware gates `/domains/veritas/*`.
- **Page metadata**: no `Metadata` export in this client page; the screen-reader
  `<h1>` reads "Headline Analyzer · Veritas · OSHUN".

## Current E2E Evidence

- `apps/oshun/web/e2e/veritas-headline-analyzer.spec.ts`
  - Desktop stories view: fixture total, category filtering, manipulation sort,
    reduced-motion card animation, keyboard story selection, and keyboard close.
  - Detail/history: selected-story contract, aggregate metrics, sorted chart
    rows, framing expansion, tracking, tracked history row/timeline, view, and
    untrack.
  - Standalone/offline mobile: PWA relaunch, offline clickbait sort, detail,
    framing expansion, tracking history, reduced motion, and no horizontal
    overflow at 390×844.

## Cross-References

- Component:
  `apps/oshun/web/src/components/domains/veritas/VeritasHeadlineAnalyzer.tsx`
- Route: `apps/oshun/web/src/app/domains/veritas/headlines/page.tsx`
- Sibling tools:
  - [`domains-veritas-articles.md`](./domains-veritas-articles.md)
  - [`domains-veritas-bias.md`](./domains-veritas-bias.md)
  - [`domains-veritas-fact-check.md`](./domains-veritas-fact-check.md)
  - [`domains-veritas-knowledge-graph.md`](./domains-veritas-knowledge-graph.md)
- Customer-facing Veritas surfaces: [`veritas.md`](./veritas.md),
  [`veritas-counterclaims.md`](./veritas-counterclaims.md)
- Feature spec: [`V1/features.md`](../../../V1/features.md#veritas)

## Open Questions / Known Gaps

- [ ] `trackedStories` is session-local only; it does not persist through reload
      or sync to a user Library/subscription store.
- [ ] No telemetry currently records story selection, sort changes, framing
      expansion, or tracking/untracking.
- [ ] Empty category-filter state is not reachable through the shipped UI
      because category chips are derived only from categories present in
      `SAMPLE_HEADLINES`.
