V1 Web PWA · Surface walkthrough

Veritas · counterclaims

A per-surface walkthrough of the V1 Web PWA customer surface: layout, states, interactions, data, and cross-references.

walked
8sections4 minread

On this page

Context. surface customer · domain veritas · route /veritas/counterclaims · auth signed-in · source apps/oshun/web/src/app/veritas/counterclaims/page.tsx

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

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#

  • LoadinggetCounterclaims() 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
  • 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 ", 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.tsxVeritasCounter (lines 1076–1349)
  • Data: apps/oshun/web/src/lib/lilith-data/veritas-depth.ts (getCounterclaims)
  • Sibling routes:
  • Internal namespace counterpart: /domains/veritas/claims/[claimId] (VeritasClaimDetailWorkspace) renders a counterclaims tab using the same domain types
  • Feature spec: V1/features.md

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)