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

# Veritas · counterclaims

## Purpose

When a story has more than one telling, Veritas does not pick. This view lays
two competing sides side by side — what each one rests on, where each is
weakest, and the Veritas verdict ("including 'not yet decided'"). The thin
server component awaits `getCounterclaims()` and renders `VeritasCounter`.

## Entry points

- **Linked from `/veritas/story`** — the timeline surfaces "contested" entries
  that point to this view (verify the link)
- **Linked from `/veritas/claim`** — when a claim has dissent (verify)
- **Direct URL / bookmark** — yes (auth required)
- **Cross-link from `/domains/veritas/claims/[claimId]`** — the counterclaims
  tab in `VeritasClaimDetailWorkspace` shares the same fixture surface (verify
  whether it links back here)

## Layout regions

`page.tsx` is a thin server component: `await getCounterclaims()` →
`<VeritasCounter>`. Mounted in `LWebShell` with `LCustomerNav active="library"`.

- **Header**: Lilith customer nav (Library tab active)
- **Masthead (`VrMast`)**:
  `Veritas · counterclaims | story · cable outage 9 mar | two sources · open dispute`,
  display title "Two sources, _disagreeing_.", italic lede
- **Section header (`VrHead`)**:
  `— · The contested claim · <data.contestedClaim>`
- **Side-by-side grid** (`auto-fit, minmax(320px, 1fr)`, marked
  `data-veritas-counterclaims-side-by-side`): one card per side
  - Per-side card (marked `data-veritas-counterclaim-side` and
    `data-veritas-counterclaim-id`):
    - Header row: `side <A|B>` code, `data.sides[].who` name (large serif),
      stance eyebrow, `VrBand` chip on the right
      (`data-veritas-counterclaim-side-quality`)
    - Pull quote (italic serif, accent left border)
    - "What the side rests on" — list of `<details>` elements per evidence item,
      first one open by default (marked
      `data-veritas-counterclaim-evidence={side-N}`); each `<summary>` has the
      evidence name + `VrBand` + `inspect` label; the open body shows
      description, inspection note, provenance, reviewer attestation, and
      excerpt hash (marked
      `data-veritas-counterclaim-evidence-inspection={side-N}`)
    - "where this side is weakest" dashed-border panel
      (`data-veritas-counterclaim-inspection-panel`): weakness copy, source
      quality rationale (`data-veritas-counterclaim-source-rationale`), reviewer
      attribution, inspected timestamp, and resolution action
      (`data-veritas-counterclaim-resolution-action`)
- **Verdict block** (`data-veritas-counterclaim-verdict`): full-width card with
  a 2px accent border:
  - "Veritas · what we have decided" eyebrow + `data.verdict.headline` (italic
    serif), updated-at code on the right
  - `data.verdict.body` paragraph
  - `data.verdict.nextSteps` rendered as " · " separated italic items

## States

- [ ] **Loading** — `getCounterclaims()` awaited server-side; no client skeleton
- [ ] **Populated (two sides)** — fixture renders both side cards plus the
      verdict block
- [ ] **Verdict: not yet decided** — verify the verdict surface for that case
      (fixture currently shows a concrete headline; check the data type)
- [ ] **Error (recoverable)** — `getCounterclaims()` rejects → nearest
      `error.tsx`
- [ ] **Offline** — cached HTML; the `<details>` toggles are native and work
      offline
- [ ] **Standalone PWA** — the grid is `auto-fit, minmax(320px, 1fr)`, so the
      cards stack on narrow screens; verify
- [x] **Empty** — N/A; the fixture always returns two sides
- [ ] **Gated** — none in this page

## Interactions

### Side card — evidence accordions

- [ ] **Evidence `<details>` summary** (per evidence item)
  - Function: native `<details>` toggle opens/closes the inspection body
  - Keyboard: Enter/Space toggles; included in tab order by default
  - Screen reader: announces "<evidence name>, <band> source quality, inspect,
    summary" (verify SR behaviour on bare `<summary>`)
  - First evidence per side is `open` by default (`open={j === 0}`)
  - Telemetry: none wired in code
- [ ] **`VrBand` chip inside summary** — non-interactive; verify SR text on the
      band swatch (currently relies on the descriptor)
- [ ] **`inspect` code label** — text only; not a button

### Side card — weakness panel

- [ ] **Static text block** — non-interactive; verify the
      `data-veritas-counterclaim-resolution-action` attribute is read by the
      shell or analytics layer

### Verdict block

- [ ] **Static card** — no buttons; the next-steps are rendered as text-only
      list items separated by " · "

## Data & contracts

- **Reads**: `getCounterclaims()` from `@/lib/lilith-data/veritas-depth` —
  returns `CounterclaimsData` ({ contestedClaim, sides[], verdict })
  - `sides[].evidence[]` includes `excerptHash`, `provenance`,
    `reviewerAttestation`, `inspectionNote`
  - `sides[].inspection.{counterclaimId, sourceQualityRationale, reviewer, inspectedAt, resolutionAction}`
- **Writes**: none
- **Realtime**: none
- **Caching**: server fetch; revalidation per fixture policy
- **Auth/role check**: shell middleware (verify)
- **Page metadata**: `title: "Veritas · counterclaims"`, description "Two
  sources, side by side, with what each rests on, where each is weakest, and
  Veritas's verdict.", `alternates.canonical: /veritas/counterclaims`

## Cross-references

- Component: `apps/oshun/web/src/components/lilith/veritas.tsx` —
  `VeritasCounter` (lines 1076–1349)
- Data: `apps/oshun/web/src/lib/lilith-data/veritas-depth.ts`
  (`getCounterclaims`)
- Sibling routes:
  - [`veritas.md`](./veritas.md) — domain hub
  - [`veritas-claim.md`](./veritas-claim.md) — claim mode for an individual
    claim
  - [`veritas-story.md`](./veritas-story.md) — story over time (contested
    entries link here)
- Internal namespace counterpart: `/domains/veritas/claims/[claimId]`
  (`VeritasClaimDetailWorkspace`) renders a counterclaims tab using the same
  domain types
- Feature spec: [`V1/features.md`](../../../V1/features.md#veritas)

## Open questions / known gaps

- [ ] The `data-veritas-counterclaim-*` attributes look like instrumentation
      hooks (analytics or E2E) but no client handler is wired in this file —
      confirm where they are consumed
- [ ] No "open full inspection" affordance besides `<details>` toggles; compare
      with `/domains/veritas/claims/[claimId]` which has an
      `Inspect     counterclaim` button + overlay (`VeritasInspectionOverlay`) —
      should this view share that flow?
- [ ] Verdict block: no action buttons (no "follow this dispute" or "open claim
      detail"); confirm whether that is intentional for the broadsheet surface
- [ ] `LCustomerNav active="library"` — consistent with `/veritas/claim` and the
      `/veritas` hub, which all use `active="library"` (no Explore inconsistency)
