# Veritas Domain — Technical Specifications

Veritas is the autonomous AI-powered news agency for Ghana and West Africa. This
document is the comprehensive technical specification of what is **implemented**
in `apps/veritas/*` and `libs/veritas/*`. Every entity, enum, endpoint, and
event below is traceable to source code; the grounding sources are listed at the
end of each major section.

This document is organized as follows: §1–§2 enumerate the applications and
libraries; §3–§4 cover the technology stack and infrastructure; §5 is the
complete domain object model (the `@veritas/core` type system); §6–§9 document
the running services (REST API, NLP, ingestion, AI workers); §10 covers the
Prisma persistence schema; §11 covers domain events; §12–§13 document the agents
system and CMS; §14 covers content authentication; §15–§16 list external
integrations and configuration; §17 states acceptance criteria.

A new engineer reading this document for the first time should start with §1
(which apps exist), then §5 (what the core data model looks like), then §6 (what
the API exposes), and then read §10 (how it is persisted) alongside whichever
service section is most relevant to their work.

---

## 1. Application Inventory

Veritas ships 13 Nx applications under `apps/veritas/`.

| Application   | Package                 | Type                | Description                                            |
| ------------- | ----------------------- | ------------------- | ------------------------------------------------------ |
| API server    | `@veritas/api`          | Hono HTTP service   | REST API, API-key + JWT auth, OpenAPI/Swagger UI       |
| NLP service   | `veritas-nlp`           | Hono HTTP service   | NLP analysis endpoints (sentiment, topics, claims, …)  |
| Ingestion     | `veritas-ingestion`     | CLI worker          | RSS/sitemap/social collection pipeline (four modes)    |
| AI workers    | `veritas-ai-workers`    | BullMQ workers      | AI content generation, processing, analysis, editorial |
| Agents        | `veritas-agents`        | Hono HTTP service   | Multi-agent orchestration service                      |
| CMS           | `veritas-cms`           | Hono HTTP service   | Editorial content-management workflow engine           |
| Video         | `veritas-video`         | Worker + HTTP       | HeyGen video production pipeline                       |
| Audio         | `veritas-audio`         | Worker              | TTS and podcast production                             |
| Social        | `veritas-social`        | Worker + HTTP       | Multi-platform social automation                       |
| Analytics     | `veritas-analytics`     | Service             | Content, user, and revenue analytics                   |
| Notifications | `veritas-notifications` | Service             | Email and FCM push delivery                            |
| Web           | `veritas-web`           | Next.js PWA         | Progressive web application                            |
| Mobile        | `veritas-mobile`        | Expo / React Native | iOS and Android application                            |

The API server's package name is `@veritas/api`; its default HTTP port comes
from the `PORT` environment variable and falls back to `3002` in
`apps/veritas/api/src/main.ts`. The NLP service reads `NLP_PORT` (default
`3002`).

_Grounding: `apps/veritas/` directory listing; `apps/veritas/api/package.json`;
`apps/veritas/api/src/main.ts`; `apps/veritas/nlp/src/config.ts`._

---

## 2. Library Inventory

Veritas ships 64 libraries under `libs/veritas/`. Libraries are organized into
six tiers by responsibility: Foundation (core types and infrastructure), Agents
(the AI newsroom), NLP/Fact-checking/Bias, Content and Knowledge, Media
Production, and Business/Monetization. The tables below list every library in
each tier with its npm package name and description.

### Foundation Libraries

| Package             | Path                    | Description                                        |
| ------------------- | ----------------------- | -------------------------------------------------- |
| `@veritas/core`     | `libs/veritas/core`     | Core domain types, branded primitives, error types |
| `@veritas/models`   | `libs/veritas/models`   | Zod validation schemas mirroring the core types    |
| `@veritas/database` | `libs/veritas/database` | Prisma client, repositories, health, metrics, seed |
| `@veritas/auth`     | `libs/veritas/auth`     | Authentication, API keys, OAuth, middleware        |
| `@veritas/cache`    | `libs/veritas/cache`    | Redis caching abstraction                          |
| `@veritas/search`   | `libs/veritas/search`   | Elasticsearch client, query builder, index manager |
| `@veritas/llm`      | `libs/veritas/llm`      | LLM client router, cost optimizer, multi-provider  |
| `@veritas/nlp-core` | `libs/veritas/nlp-core` | NLP primitives and shared utilities                |
| `@veritas/events`   | `libs/veritas/events`   | Domain event bus (publisher/subscriber)            |

### Agent Libraries

| Package                         | Description                                          |
| ------------------------------- | ---------------------------------------------------- |
| `@veritas/agents-core`          | Base agent framework, LLM client, message bus, state |
| `@veritas/agents-orchestrator`  | Multi-agent workflow orchestration, registry, router |
| `@veritas/agents-editorial`     | Editor-in-Chief, Managing Editor, Content Strategist |
| `@veritas/agents-journalism`    | Investigative, political, breaking, business agents  |
| `@veritas/agents-fact-checking` | Fact-checker agent with web evidence search          |
| `@veritas/agents-social-media`  | Social media manager agent                           |
| `@veritas/agents-devops`        | DevOps and infrastructure management agent           |
| `@veritas/agents-product`       | Product management agent                             |
| `@veritas/agents-qa`            | QA and testing automation agent                      |

### NLP, Fact-Checking, and Bias Libraries

| Package                           | Description                             |
| --------------------------------- | --------------------------------------- |
| `@veritas/ghana-nlp`              | Ghana NLP (Khaya) API client            |
| `@veritas/bias-detection`         | Political bias analysis and GPL scoring |
| `@veritas/fact-checking`          | Claim verification and evidence scoring |
| `@veritas/claims`                 | Claim extraction and linking            |
| `@veritas/content-classification` | Topic and category classification       |

### Content and Knowledge Libraries

| Package                       | Description                                     |
| ----------------------------- | ----------------------------------------------- |
| `@veritas/rag`                | RAG pipeline (indexing, retrieval, archive)     |
| `@veritas/knowledge-graph`    | Entity graph, profiles, relationships           |
| `@veritas/story-clustering`   | Semantic story grouping and timeline            |
| `@veritas/recommendations`    | Collaborative/content-based filtering           |
| `@veritas/headline-service`   | Headline generation and A/B testing             |
| `@veritas/article-generation` | AI article drafting pipeline                    |
| `@veritas/content-auth`       | Content authenticity, hashing, C2PA, provenance |
| `@veritas/cms`                | CMS workflow engine                             |
| `@veritas/ingestion-core`     | Core ingestion pipeline primitives              |
| `@veritas/ai-next-gen`        | Investigations, OSINT, real-time anchors        |
| `@veritas/automated-content`  | Weather, market, fuel-price, traffic generators |
| `@veritas/archive`            | Content archival and retrieval                  |
| `@veritas/research-assistant` | Research tools for journalists                  |

### Media Production Libraries

| Package                      | Description                                     |
| ---------------------------- | ----------------------------------------------- |
| `@veritas/video-production`  | HeyGen video composition and rendering pipeline |
| `@veritas/audio-production`  | ElevenLabs/Ghana NLP TTS and mastering pipeline |
| `@veritas/live-stream`       | YouTube Live, RTMP, graphics, interactive Q&A   |
| `@veritas/social-automation` | Cross-platform social media posting             |

### Business and Monetization Libraries

| Package                      | Description                                      |
| ---------------------------- | ------------------------------------------------ |
| `@veritas/analytics`         | Content, user, revenue, social analytics         |
| `@veritas/payments`          | Stripe, Paystack, mobile money integration       |
| `@veritas/billing`           | Subscriptions, invoicing, pricing, usage         |
| `@veritas/notifications`     | Push (FCM), email, SMS notification delivery     |
| `@veritas/b2b-sdk`           | TypeScript SDK for B2B API consumers             |
| `@veritas/b2b-sdk-python`    | Python SDK for B2B consumers                     |
| `@veritas/api-client`        | Internal/shared API client                       |
| `@veritas/newsletter`        | Email newsletter campaigns                       |
| `@veritas/seo`               | SEO optimization and sitemap generation          |
| `@veritas/business`          | Profitability and business optimization          |
| `@veritas/expansion`         | Pan-African geographic expansion                 |
| `@veritas/developer-support` | AI-powered developer support chatbot for B2B API |
| `@veritas/signup`            | User registration flows                          |

### Regional, Access, and Infrastructure Libraries

| Package                  | Description                                  |
| ------------------------ | -------------------------------------------- |
| `@veritas/regional`      | Region-specific content and configuration    |
| `@veritas/ussd`          | USSD channel for telco news delivery         |
| `@veritas/platforms`     | Platform SDKs for smartwatch, TV, enterprise |
| `@veritas/ab-testing`    | A/B testing for headlines and content        |
| `@veritas/community`     | Community engagement features                |
| `@veritas/comments`      | Article commenting system                    |
| `@veritas/emergency`     | Emergency broadcast and alert distribution   |
| `@veritas/storage`       | File storage abstraction (S3/MinIO/GCS)      |
| `@veritas/realtime-data` | Real-time data feeds and streaming           |
| `@veritas/risk`          | Risk assessment and content safety           |
| `@veritas/operations`    | Security, compliance, QA, monitoring ops     |

_Grounding: `libs/veritas/` directory listing (64 entries)._

---

## 3. Technology Stack

### Languages and Runtimes

| Technology | Purpose                                           |
| ---------- | ------------------------------------------------- |
| TypeScript | All TypeScript packages (strict mode, ESM)        |
| Node.js    | `>= 22` — server runtime for all backend services |
| Python     | B2B Python SDK (`@veritas/b2b-sdk-python`)        |

### Frameworks and Key Dependencies

| Dependency                   | Used by                | Purpose                           |
| ---------------------------- | ---------------------- | --------------------------------- |
| `hono` + `@hono/node-server` | api, nlp, agents, cms  | HTTP server                       |
| `@hono/swagger-ui`           | api                    | Swagger UI documentation page     |
| `@hono/zod-openapi`          | api                    | Zod-validated OpenAPI integration |
| `zod`                        | models, api, nlp       | Runtime schema validation         |
| `@prisma/client` + `prisma`  | database               | ORM and migrations                |
| `pg`                         | api, ingestion         | Raw PostgreSQL access             |
| `ioredis`                    | api, agents, ingestion | Redis client                      |
| `@elastic/elasticsearch`     | api, search            | Elasticsearch client              |
| `stripe`                     | api, payments          | Card payments                     |
| `bcryptjs`                   | api                    | Password hashing                  |

_Grounding: `apps/veritas/api/package.json`; `libs/veritas/core/package.json`;
`libs/veritas/database/package.json`._

---

## 4. Infrastructure Requirements

### Core Infrastructure

| Service        | Purpose                                             |
| -------------- | --------------------------------------------------- |
| PostgreSQL     | Primary database, with `pgvector` and `citext`      |
| Redis          | Caching, BullMQ job queues, sessions, agent pub/sub |
| Elasticsearch  | Full-text article search                            |
| MinIO / AWS S3 | Media asset storage                                 |

### PostgreSQL Extensions

The Prisma datasource enables two extensions via
`previewFeatures = ["postgresqlExtensions"]`:

| Extension  | Mapped name | Purpose                                            |
| ---------- | ----------- | -------------------------------------------------- |
| `pgvector` | `vector`    | Vector embeddings for semantic deduplication / RAG |
| `citext`   | `citext`    | Case-insensitive text                              |

### Database Configuration

| Property            | Value                                               |
| ------------------- | --------------------------------------------------- |
| Connection variable | `VERITAS_DATABASE_URL`                              |
| ORM                 | Prisma (`prisma-client-js` generator)               |
| Schema location     | `libs/veritas/database/prisma/schema.prisma`        |
| Generated SQL       | `libs/veritas/database/prisma/generated/schema.sql` |

The Prisma schema defines roughly 75 models and 50 enums (see §10 Persistence).

_Grounding: `libs/veritas/database/prisma/schema.prisma` lines 1–17; model/enum
declarations._

---

## 5. Domain Object Model (`@veritas/core`)

`@veritas/core` is the canonical type layer — the single source of truth for
every domain entity, enum, and primitive in the Veritas system. All other
libraries import types from here rather than defining their own.
`@veritas/models` provides matching Zod schemas
(`libs/veritas/models/src/schemas/`) so that API boundaries can validate
incoming and outgoing data against these types at runtime.

The sections below document each type file in `libs/veritas/core/src/`. The
grounding note at the end of §5 lists every source file referenced.

### 5.1 Branded Primitives (`common.ts`)

Branded primitives prevent accidental mixing of semantically distinct string
values (e.g., passing a raw string where a validated URL is expected). Each
primitive has a factory function and a type guard. The table below lists the
four primitives and their validation rules.

| Type          | Underlying | Validation regex / rule                                  |
| ------------- | ---------- | -------------------------------------------------------- |
| `Uuid`        | `string`   | `isValidUuid` — 8-4-4-4-12 hex UUID                      |
| `UrlString`   | `string`   | `isValidUrlString` — `^https?://.+`                      |
| `CountryCode` | `string`   | `isValidCountryCode` — `^[A-Z]{2}$` (ISO 3166-1 alpha-2) |
| `Slug`        | `string`   | `isValidSlug` — `^[a-z0-9]+(?:-[a-z0-9]+)*$`             |

Factory functions: `createUuid`, `createUrlString`, `createCountryCode`,
`createSlug`. Shared interfaces: `Timestamped` (`createdAt`, `updatedAt`),
`Cursor<T>`, `PaginatedResponse<T>`, `PaginationParams`, `WithMetadata`.
`Result<T, E>` / `AsyncResult<T, E>` are discriminated success/failure unions.
`Metadata` is `Record<string, unknown>`.

### 5.2 Language (`language.ts`)

The language system encodes Ghana's multilingual reality directly into the type
layer. The eight BCP 47 language tags drive content routing, TTS provider
selection, and search index configuration. `CodeSwitchingResult` models the
common Ghanaian pattern of mixing English with a local language mid-sentence.

`LANGUAGE_TAGS` (BCP 47) — 8 values:

| Tag   | Name         | Primary regions                                        |
| ----- | ------------ | ------------------------------------------------------ |
| `en`  | English      | Greater Accra, Ashanti, Western, Eastern, Central      |
| `tw`  | Twi (Akan)   | Ashanti, Eastern, Greater Accra, Bono, Ahafo           |
| `ee`  | Ewe          | Volta, Oti, Greater Accra                              |
| `gaa` | Ga           | Greater Accra                                          |
| `ha`  | Hausa        | Northern, Upper East, Upper West, North East, Savannah |
| `dag` | Dagbani      | Northern, North East, Savannah                         |
| `fr`  | French       | Upper West, Upper East                                 |
| `und` | Undetermined | —                                                      |

`type Language` is an alias of `LanguageTag`. `TRANSLATION_QUALITIES` =
`machine | human | verified`. `Translation` carries `id`, `languageTag`,
`headline`, `summary`, `body`, `bodyHtml`, `quality`, `metadata`.
`CodeSwitchingResult` reports `primaryLanguage`, `secondaryLanguages`,
`confidence`, `hasCodeSwitching`, and `languageProportions`. `LocaleConfig` and
`DEFAULT_LOCALE_CONFIGS` define per-language date/time/number formatting; all
eight default to country `GH`, `ltr` direction.

### 5.3 Article (`article.ts`)

`Article` is the central entity in the domain model. It carries the full
lifecycle of a piece of journalism: from raw ingestion metadata (`sourceUrl`,
`sourcePublishedAt`) through editorial enrichment (`claims`, `biasScore`,
`perspectives`) to publication and archival. The table below lists every field;
fields marked `null` are optional or unset at ingestion time and populated
progressively by the pipeline.

`ARTICLE_STATUSES` —
`draft | pending_review | published | scheduled | archived | retracted`.

`ARTICLE_CONTENT_TYPES` —
`news | analysis | opinion | feature | interview | investigation | breaking | live_update | explainer | fact_check`.

**`Article`** (`extends Timestamped`) — fields:

| Field               | Type                      | Notes                      |
| ------------------- | ------------------------- | -------------------------- |
| `id`                | `Uuid`                    | required                   |
| `slug`              | `Slug`                    | required                   |
| `status`            | `ArticleStatus`           | required                   |
| `contentType`       | `ArticleContentType`      | required                   |
| `headline`          | `string`                  | required                   |
| `summary`           | `string \| null`          | lede                       |
| `body`              | `string`                  | plain text                 |
| `bodyHtml`          | `string \| null`          | rendered HTML              |
| `featuredImage`     | `MediaAsset \| null`      | hero image                 |
| `media`             | `MediaAsset[]`            | all assets                 |
| `audioVersion`      | `MediaAsset \| null`      | audio rendering            |
| `videoVersion`      | `MediaAsset \| null`      | video rendering            |
| `categories`        | `Category[]`              |                            |
| `tags`              | `Tag[]`                   |                            |
| `entities`          | `Entity[]`                | named entities             |
| `topics`            | `Topic[]`                 |                            |
| `source`            | `ArticleSource \| null`   | originating source ref     |
| `sourceUrl`         | `UrlString \| null`       |                            |
| `sourcePublishedAt` | `Date \| null`            |                            |
| `authors`           | `AuthorRef[]`             |                            |
| `attributions`      | `ArticleAttribution[]`    | aggregated-content credits |
| `isOriginal`        | `boolean`                 |                            |
| `isAIGenerated`     | `boolean`                 |                            |
| `humanReviewed`     | `boolean`                 |                            |
| `reviewedBy`        | `UserRef \| null`         |                            |
| `factCheckStatus`   | `FactCheckStatus \| null` |                            |
| `claims`            | `Claim[]`                 |                            |
| `biasScore`         | `BiasScore \| null`       |                            |
| `perspectives`      | `Perspective[]`           | alternative perspectives   |
| `storyClusterId`    | `Uuid \| null`            |                            |
| `relatedArticleIds` | `Uuid[]`                  |                            |
| `languageTag`       | `LanguageTag`             |                            |
| `translations`      | `Translation[]`           |                            |
| `publishedAt`       | `Date \| null`            |                            |
| `scheduledAt`       | `Date \| null`            |                            |
| `views`             | `number`                  |                            |
| `shares`            | `number`                  |                            |
| `readTimeSeconds`   | `number`                  | estimated read time        |
| `seo`               | `ArticleSEO \| null`      |                            |
| `metadata`          | `Metadata`                |                            |

Supporting types: `ArticleSource` (`id`, `name`, `domain`, `url`), `ArticleSEO`
(`metaTitle`, `metaDescription`, `canonicalUrl`, `ogImage`, `twitterCard` ∈
`summary | summary_large_image`, `focusKeywords`, `schemaType` ∈
`NewsArticle | Article | ReportageNewsArticle`). `ArticleListItem` and
`ArticleDetail` (extends list item) are read projections; `ArticleFeedItem` adds
`recommendationReason`
(`trending | following | similar | breaking | personalized | editorial`),
`score`, `hasInteracted`, `isFollowed`. `ArticleEngagement` carries
view/share/comment/save counts and a `scrollDepth` distribution
(`25% / 50% / 75% / 100%`). `ArticleSearchParams` defines query/filter fields
with `sortBy` ∈ `relevance | recency | popularity | engagement`.

**Article versioning** — `ARTICLE_CHANGE_TYPES`
(`created | updated | published | unpublished | corrected | retracted | restored | metadata_updated | media_updated | translation_added | auto_saved`).
`ArticleVersion` snapshots content per revision; `ArticleVersionDiff` records
changed fields and `TextDiff` operations (`equal | insert | delete`);
`ArticleVersionComparison`, `ArticleVersionHistory`, `ArticleVersionSummary`
round out the revision API.

**Article media** — `ARTICLE_MEDIA_PLACEMENTS`
(`hero | inline | gallery | sidebar | pullquote | background | thumbnail`).
`ArticleMedia`, `ArticleMediaGallery` (layout
`grid | carousel | masonry | slideshow`), and `ArticleMediaSummary` model
in-article media placement.

DTOs: `ArticleCreateInput`, `ArticleUpdateInput`.

### 5.4 Source (`source.ts`)

A `Source` represents a monitored news outlet. `SourceFeed` represents one
polling endpoint within that outlet (an outlet may have multiple RSS feeds).
`SourceCredibility` is the multi-signal credibility assessment that the system
continuously updates as the source publishes articles that are later verified or
refuted. Note that the Prisma `SourceKind` enum (§10) is broader than the
`SOURCE_KINDS` in `@veritas/core`; both are documented below.

`SOURCE_KINDS` (core type) —
`publisher | government | ngo | blog | social | academic | wire_service | other`.

`SOURCE_STATUSES` — `active | paused | retired | pending`.

`OWNERSHIP_TYPES` —
`private | state | public | political | religious | ngo | community | academic | unknown | other`.

`FEED_TYPES` — `rss | atom | sitemap | api | scrape`.

**`Source`** (`extends Timestamped`) — `id`, `name`, `kind`, `status`,
`canonicalDomain`, `homeUrl`, `countryCode`, `region`, `languages`, `notes`,
`ownership` (`SourceOwnership`), `bias` (`SourceBiasMetadata`), `metadata`.

**`SourceFeed`** (`extends Timestamped`) — `id`, `sourceId`, `feedType`, `url`,
`isActive`, `fetchIntervalSeconds`, `priority`, `lastFetchedAt`,
`lastHttpStatus`, `etag`, `lastModified`, `consecutiveFailures`, `backoffUntil`,
`metadata`.

**`SourceBiasMetadata`** — `politicalScore` (−1…1), `confidence` (0…1),
`methodology`, `lastAssessedAt`.

**`SourceReliability`** — `score` (0–100), `factualAccuracy`, `correctionRate`,
`transparency`, `factCheckedCount`, `lastAssessedAt`.

`SOURCE_CREDIBILITY_TIERS` —
`highly_credible | generally_credible | mixed_credibility | low_credibility | unreliable | unrated`.

**`SourceCredibility`** is a multi-signal assessment: `tier`, `overallScore`,
`factualAccuracy`, `editorialStandards`, `transparency`, `correctionPractices`,
`sourceAttribution`, `clickbaitFrequency`, `sensationalism`, `totalFactChecked`,
`verifiedTrueCount`, `verifiedFalseCount`, `misleadingCount`,
`correctionsIssued`, `retractionsIssued`, `externalRatings`
(`SourceExternalRating[]`), `notes`, `redFlags` (`SourceCredibilityRedFlag[]`),
`lastAssessedAt`, `methodologyVersion`.

**`SourceBias`** (extended) adds `articlesAnalyzed`, `topicBreakdown`,
`historicalTrend`, and `indicators` whose `type` ∈
`loaded_language | source_selection | story_selection | omission | placement | headline_framing | image_selection`.
`SourceCredibilityRedFlag.type` ∈
`known_misinformation | undisclosed_ownership | deceptive_practices | content_farm | propaganda | satire_misrepresented | ai_generated_mass | plagiarism_pattern | state_controlled | advertising_disguised`.

Other types: `SourceCoverage`, `SourceRegistryEntry`,
`SourcePerformanceMetrics`. DTOs: `SourceCreateInput`, `SourceUpdateInput`,
`SourceFeedCreateInput`.

> Note: the Prisma `SourceKind` enum is broader than the core type — see §10.

### 5.5 Fact-Check (`fact-check.ts`)

The fact-check type system models the complete verification lifecycle: claim
extraction from article text (`Claim`), evidence retrieval and scoring
(`ClaimEvidence`), verdict assignment (`ClaimVerification`), and the compiled
report (`FactCheckReport`). Verdicts carry both a status label and a confidence
score so downstream consumers can threshold on confidence as well as verdict.

`FACT_CHECK_OVERALL_STATUSES` —
`verified | likely_true | disputed | misleading | mostly_false | false | unverifiable | unverified`.
Each status has a display label in `FACT_CHECK_STATUS_LABELS` and a hex color in
`FACT_CHECK_STATUS_COLORS`.

`CLAIM_TYPES` —
`factual | opinion | prediction | quote | statistical | historical | scientific`.

**`Claim`** (`extends Timestamped`) — `id`, `text`, `normalizedText`, `type`,
`articleId`, `speaker`, `context`, `checkworthiness` (0–1),
`extractionConfidence` (0–1), `metadata`.

**`ClaimEvidence`** (`extends Timestamped`) — `id`, `claimId`, `url`,
`sourceId`, `quote`, `title`, `summary`, `stance`
(`supports | refutes | neutral | inconclusive`), `relevance` (0–1),
`credibility` (0–1), `retrievedAt`, `metadata`.

**`ClaimVerification`** (`extends Timestamped`) — `id`, `claimId`, `status`
(`FactCheckOverallStatus`), `confidence`, `evidenceIds`, `summary`, `notes`,
`verifiedBy`, `metadata`.

**`FactCheckStatus`** (article-level) — `overall`, a `claims` array of
`{ claimId, text, status, confidence }`, `lastCheckedAt`, `checkedBy`.

**`FactCheckReport`** (`extends Timestamped`) — `id`, `articleId`, `claims`,
`verifications`, `evidence`, `overallVerdict`, `summary`, `methodology`,
`authorId`, `status` (`draft | review | published`), `publishedAt`.

`ClaimMatch` reports similarity and `matchType`
(`exact | paraphrase | related | contradicts`). `CheckworthinessFactors` and
`CheckworthinessAssessment` score whether a claim is worth checking (`priority`
∈ `low | medium | high | urgent`).

`CORRECTION_KINDS` — `correction | clarification | update | retraction`.
`CORRECTION_SEVERITIES` — `low | medium | high`. **`ArticleCorrection`**
(`extends Timestamped`) — `id`, `articleId`, `sourceId`, `kind`, `severity`,
`summary`, `publicNote`, `publishedAt`, `metadata`.

### 5.6 Bias (`bias.ts`)

The bias type system models both the continuous political score (a float on
−1…+1) and the discrete Ghana Political Lean (GPL) band (an integer on −3…+3
mapping to NDC vs. NPP alignment). The discrete band is used for display and
editorial reporting; the continuous score is used for computation and trending.
The `CoverageBlindspot` type models the complementary problem of what the
newsroom is _not_ covering.

**`BiasScore`** — `political` (−1…1), `regional` (`string[]`), `confidence`,
`methodology`, `lastAssessedAt`.

**Ghana Political Spectrum** —
`GHANA_POLITICAL_AXIS_VERSION = 'gh_political_axis_v1'`.
`GHANA_POLITICAL_BIAS_BANDS` is the integer set `[-3, -2, -1, 0, 1, 2, 3]`,
where −3 = Strong NDC lean … +3 = Strong NPP lean.
`toGhanaPoliticalBiasBand(score)` clamps and rounds a continuous score;
`ghanaPoliticalAlignment(band)` maps a band to `npp | ndc | independent`;
`ghanaPoliticalBandLabel(band)` returns the human label.

`BIAS_ASSESSMENT_METHODS` —
`automated_nlp | manual_review | crowd_sourced | expert_panel | algorithmic | hybrid`.

**`BiasAssessment`** — `id`, `targetType` (`article | source`), `targetId`,
`overallScore` (`BiasScore`), `ghanaPoliticalBand`, `method`,
`linguisticIndicators` (`LinguisticBiasIndicators`), `coverageIndicators`
(`CoverageBiasIndicators`), `notes`, `assessedAt`, `assessedBy`.

`PERSPECTIVE_STANCES` — `supporting | critical | neutral | analysis`.
`Perspective` carries `source`, `url`, `stance`, `summary`, `metadata`.
`BiasCorrection` suggests balancing actions (`biasType` ∈
`political | regional | source | linguistic | coverage`). `CoverageBlindspot`
(`type` ∈ `topic | region | perspective | source_type`) and `NewsDietAnalysis`
model under-coverage detection.

### 5.7 Entity (`entity.ts`)

Entities are the named real-world things mentioned in articles: people,
organizations, places, and events. The system links entity mentions across
articles (entity linking) and maintains structured sub-types for Ghana-specific
entities such as political figures with their position, party, and constituency.

`ENTITY_TYPES` —
`person | organization | place | event | product | work | concept | legislation | other`.

**`Entity`** — `id`, `type`, `name`, `normalizedName`, `summary`, `externalIds`
(`EntityExternalIds`: `wikidata`, `wikipedia`, `dbpedia`, `freebase`, `custom`),
`metadata`.

`EntityMention` records an entity occurrence in text (`startOffset`,
`endOffset`, `confidence`, `salience`). Subtypes: `PersonEntity`,
`OrganizationEntity` (`orgType` ∈
`government | political_party | ngo | company | media | academic | other`),
`PlaceEntity` (`placeType` ∈
`country | region | city | district | landmark | other`), `EventEntity`
(`eventType` ∈
`election | conference | protest | disaster | ceremony | sports | other`).
`EntityCandidate` / `EntityResolutionResult` model entity linking.

**Ghana-specific** — `GHANA_POLITICAL_POSITIONS`
(`president | vice_president | minister | deputy_minister | mp | mce | dce | regional_minister | chief_of_staff | speaker | chief_justice`).
`GhanaPoliticalFigure` extends `PersonEntity` with `politicalData` (`position`,
`party` ∈ `npp | ndc | cpp | other`, `constituency`, `region`, `tenureStart`,
`tenureEnd`).

### 5.8 Media (`media.ts`)

`MediaAsset` is the base type for all media attached to articles: images,
videos, audio renders, embeds, and documents. Sub-types add provider-specific
fields (e.g., `VideoAsset` carries HLS/DASH URLs; `AudioAsset` carries a
waveform for the audio player). Storage is abstracted over multiple backends
(`s3`, `gcs`, `r2`, `minio`, `local`, `cdn`).

`MEDIA_KINDS` — `image | video | audio | embed | document | infographic`.
`MEDIA_ROLES` —
`featured | inline | thumbnail | gallery | social | hero | background | other`.
`MEDIA_STORAGE_PROVIDERS` — `external | s3 | gcs | r2 | minio | local | cdn`.
`MEDIA_SOURCES` — `extracted | uploaded | generated | licensed`.

**`MediaAsset`** — `id`, `kind`, `role`, `source`, `originalUrl`, `cdnUrl`,
`mimeType`, `width`, `height`, `durationSeconds`, `sizeBytes`, `checksum`,
`altText`, `caption`, `credit`, `storage` (`MediaStorage`), `metadata`.

Subtypes: `VideoAsset` (extends `MediaAsset`; `transcodeStatus` ∈
`pending | processing | completed | failed`, `variants`, `hlsUrl`, `dashUrl`,
`thumbnail`), `AudioAsset` (`variants`, `waveform`, `transcript`), `EmbedAsset`
(`provider` ∈
`youtube | vimeo | twitter | facebook | instagram | tiktok | spotify | soundcloud | other`).
`ResponsiveImage` / `ImageVariant` support responsive image delivery.

### 5.9 Taxonomy (`taxonomy.ts`)

Taxonomy organizes articles into a three-level hierarchy: broad `Category`
(e.g., politics, sports), more specific `Topic` (with trending detection), and
free-form `Tag`. The 12 seed categories (`DEFAULT_CATEGORIES`) and 16 Ghana
administrative regions (`GHANA_REGIONS`) are hard-coded constants so that
classification models and the UI can reference them by stable values.

`Category` (`extends Timestamped`) — hierarchical via `parentId`; `CategoryTree`
adds `children`, `depth`, `path`. `Topic` (`extends Timestamped`) — more
specific than category, carries `isTrending` and `trendScore`. `Tag`
(`extends Timestamped`) — free-form, with `usageCount`.

`DEFAULT_CATEGORIES` — 12 seed categories: politics, business, sports,
entertainment, technology, health, education, environment, crime, opinion,
regional, international.

`GHANA_REGIONS` — all 16 administrative regions, each with `slug`, `name`,
`code` (e.g. `greater-accra`/`GA`, `ashanti`/`AS`, … `western-north`/`WN`).

Helpers: `buildCategoryPath`, `getCategoryDescendants`. Statistics types:
`CategoryStats`, `TopicStats`.

### 5.10 Story Cluster (`story-cluster.ts`)

A `StoryCluster` groups articles from different outlets that cover the same
underlying event. Clustering can be triggered by semantic similarity, entity
overlap, temporal proximity, or a hybrid of all three. `StoryTimeline` and
`StoryTimelinePoint` give readers a chronological view of how a story developed.
`StoryImportanceScore` feeds the recommendation engine with a newsworthiness
signal beyond recency.

`CLUSTERING_METHODS` —
`semantic_similarity | entity_overlap | temporal_proximity | topic_modeling | hybrid | manual`.
`STORY_CLUSTER_STATUSES` — `active | developing | archived | merged`.

**`StoryCluster`** (`extends Timestamped`) — `id`, `title`, `summary`, `status`,
`method`, `score`, `canonicalArticleId`, `articleIds`, `lastActivityAt`,
`archivedAt`, `mergedIntoId`, `metadata`.

`StoryTimeline` / `StoryTimelinePoint` (`eventType` ∈
`breaking | update | analysis | reaction | follow_up`) build chronological story
views. `StoryCoverageAnalysis` reports source/regional/language distribution and
a perspective balance. `StoryImportanceFactors` / `StoryImportanceScore` score
newsworthiness (timeliness, impact, prominence, proximity, conflict, novelty,
human interest). `ClusterMergeRequest`, `RelatedStoryRecommendation`,
`StorySearchParams`, `StorySearchResult` complete the API.

### 5.11 Author (`author.ts`)

Authors can be either external correspondents (human writers from ingested
sources) or internal Veritas contributors. `AuthorProfile` is the public- facing
profile used on the website; `ArticleAttribution` handles syndication credit for
aggregated content.

`Author` (`extends Timestamped`) — `id`, `name`, `slug`, `bio`, `avatarUrl`,
`sourceId`, `socialLinks`, `expertise`, `metadata`. `AuthorRef` and `UserRef`
are lightweight references. `ArticleAttribution` records syndication credit.

`CONTRIBUTOR_ROLES` —
`author | editor | reviewer | translator | photographer | illustrator | contributor`.
`AUTHOR_VERIFICATION_STATUSES` — `unverified | pending | verified | notable`.

`AuthorProfile` (extends `Author`) is the public-profile entity with
`credentials`, `affiliations`, `beats`, `awards`, `profileStats`,
`profileCompleteness`. DTOs: `AuthorCreateInput`, `AuthorUpdateInput`,
`AuthorProfileUpdateInput`.

### 5.12 Errors (`errors.ts`)

All Veritas services use a shared error hierarchy so that API responses are
consistent and clients can write a single error-handling path. `DomainError`
carries a `DOMAIN_ERROR_CODES` code for programmatic discrimination;
`normalizeError` and `createErrorResponse` are helpers used by the Hono error
handler in `veritas-api`.

`@veritas/core` exports HTTP error classes (`HttpError`, `BadRequestError`,
`UnauthorizedError`, `ForbiddenError`, `NotFoundError`, `ConflictError`,
`ValidationError`, `RateLimitError`, `InternalError`,
`ServiceUnavailableError`), a `DomainError` class with `DOMAIN_ERROR_CODES`, and
helpers `normalizeError` and `createErrorResponse`.

_Grounding:
`libs/veritas/core/src/{common,language,article,source,fact-check, bias,entity,media,taxonomy,story-cluster,author,errors,index}.ts`._

---

## 6. REST API (`@veritas/api`)

`veritas-api` is the single HTTP gateway for all external traffic. Web, mobile,
and B2B clients speak only to this service; it proxies no other internal
services directly but shares database access via the common library layer. All
routes are versioned under `/api/v1`. The service exposes 29 route groups
covering the full product surface: editorial content, analytics, payments,
compliance, and B2B access.

### 6.1 Server Configuration

- **Framework**: Hono with `@hono/node-server`.
- **Route mounting**: all v1 routes are mounted under **`/api/v1`** (see
  `registerV1Routes`, which creates a sub-`Hono` and calls
  `app.route('/api/v1', v1)`).
- **Middleware** (`server.ts`, applied in order): request `logger` (optional),
  `timing`, `secureHeaders`, `cors` (optional), Redis-backed rate limiting on
  `/api/*` (optional), sandbox detection on `/api/*`, API-key authentication on
  `/api/*` (optional), and a global `errorHandler`.
- **Auth**: API-key authentication middleware (`X-API-Key`); a separate JWT
  access-token mechanism backs the `/auth/*` routes
  (`infrastructure/auth/accessToken.ts`).
- **Sandbox mode**: requests with test API keys (`sk_test_*`) are detected by
  `sandboxMiddleware`; `X-Sandbox-*` headers tune scenario, seed, latency, and
  error injection. A dedicated `/api/v1/sandbox` route group is mounted when a
  Postgres pool is available.
- **OpenAPI**: `generateOpenApiSpec` emits an OpenAPI document served at
  `GET /openapi.json`, with Swagger UI at `GET /docs` (and `/docs/*`). The spec
  declares a single server with base URL `/api/v1`.

### 6.2 Route Groups

All paths below are relative to `/api/v1`. Methods are taken directly from the
route handler source files. The tables are organized by functional area; within
each area, read the route list as a contract — what the API promises to provide.

**Note on the health route:** the `/health` and `/ready` liveness/readiness
endpoints are mounted at the app root (not under `/api/v1`), so they are
reachable without an API key for use by load balancer health checks.

**Health** (`routes/health.ts`, mounted at app root, not under `/api/v1`):
liveness/readiness endpoints.

**Auth** (`auth.ts`):

| Method | Path             | Purpose                    |
| ------ | ---------------- | -------------------------- |
| POST   | `/auth/register` | Register a reader account  |
| POST   | `/auth/login`    | Log in, issue access token |
| GET    | `/auth/me`       | Current authenticated user |
| POST   | `/auth/logout`   | Log out                    |

**Articles** (`articles.ts`):

| Method | Path                       | Purpose                         |
| ------ | -------------------------- | ------------------------------- |
| GET    | `/articles`                | List articles                   |
| GET    | `/articles/trending`       | Trending articles               |
| GET    | `/articles/search`         | Search articles                 |
| GET    | `/articles/search/suggest` | Search autocomplete suggestions |
| GET    | `/articles/:id`            | Article detail                  |
| GET    | `/articles/:id/related`    | Related articles                |

**Sources** (`sources.ts`):

| Method | Path                               | Purpose                       |
| ------ | ---------------------------------- | ----------------------------- |
| GET    | `/sources`                         | List sources                  |
| GET    | `/sources/bias-spectrum`           | Sources arranged by bias      |
| GET    | `/sources/media-groups`            | Media-group groupings         |
| GET    | `/sources/cross-ownership`         | Cross-ownership relationships |
| GET    | `/sources/:id`                     | Source detail                 |
| GET    | `/sources/:id/transparency`        | Transparency record           |
| GET    | `/sources/:id/corrections`         | Source corrections            |
| GET    | `/sources/:id/bias`                | Source bias assessment        |
| GET    | `/sources/:id/ownership`           | Ownership detail              |
| GET    | `/sources/:id/regional-emphasis`   | Regional coverage emphasis    |
| GET    | `/sources/:id/regional-comparison` | Regional comparison           |

**Claims and fact-checking** (`claims.ts`):

| Method | Path                   | Purpose                 |
| ------ | ---------------------- | ----------------------- |
| GET    | `/claims`              | List claims             |
| GET    | `/claims/:id`          | Claim detail            |
| GET    | `/claims/:id/articles` | Articles citing a claim |
| POST   | `/fact-check`          | Run a fact check        |

**Bias** (`bias.ts`):

| Method | Path                 | Purpose                         |
| ------ | -------------------- | ------------------------------- |
| POST   | `/bias/score`        | Score text for political bias   |
| GET    | `/articles/:id/bias` | Get an article's bias score     |
| POST   | `/articles/:id/bias` | Compute/store an article's bias |

The bias routes use a pluggable `PoliticalBiasScorer` from `AppContext`; when
not wired, they fall back to a Ghana keyword baseline in
`domain/political-bias-scorer.ts`.

**Stories** (`stories.ts`): `GET /stories`, `GET /stories/:id`,
`GET /stories/:id/articles`.

**Taxonomy** (`taxonomy.ts`): `GET /categories`, `GET /topics`.

**Media renders** (`audio.ts`, `video.ts`): `GET /audio`, `GET /video`.

**Voice / Avatar profiles** (`voiceProfiles.ts`, `avatarProfiles.ts`): voice and
avatar profile management.

**Writing assistant** (`writingAssistant.ts`): `POST /writing-assistant`.

**Tips** (`tips.ts`): `POST /tips` — encrypted reader tip submission.

**Home-feed preferences** (`preferences.ts`):
`GET / PUT / PATCH /preferences/home-feed`.

**Reading mode** (`reading-mode.ts`): reading-mode preferences.

**Compliance — NMC** (`compliance.ts`): 14 endpoints under `/compliance/nmc/*`
covering guidelines, violations, remedies, complaint stages, content checks
(`POST /compliance/nmc/check`, `.../check/batch`), complaints CRUD and
transitions, per-source compliance scores, statistics, and ethics references.

**Data protection** (`data-protection.ts`): DPA (Data Protection Act) consent
and subject-request endpoints.

**Election coverage** (`election-coverage.ts`): 11 endpoints under `/election/*`
— constitutional references, guidelines, offences, parties, phase detection,
coverage checks (`POST /election/check`), active elections, configuration, and
per-election violations and balance reports.

**Payments — Mobile Money** (`payments.ts`): 13 endpoints under
`/payments/momo/*` (request, status, pre-approval, refund, validate, balance,
collection/disbursement/pre-approval callbacks) plus
`/payments/transactions/:userId` and `/payments/subscriptions/:userId`.

**Payments — Vodafone / AirtelTigo / Stripe** (`vodafone-payments.ts`,
`airteltigo-payments.ts`, `stripe-payments.ts`): provider-specific payment
routes.

**Subscriptions** (`subscriptions.ts`): ~26 endpoints — plan catalog
(`/subscriptions/plans`, `.../plans/:planId`), current subscription, lifecycle
(`subscribe`, `upgrade`, `downgrade`, `cancel`, `resume`, `pause`, `unpause`),
history, usage metering (`usage`, `usage/monthly`, `usage/increment`), feature
access checks, family-plan invitations and membership, and student verification.

**Paywall** (`paywall.ts`): paywall configuration and metering.

**Subscription analytics** (`subscription-analytics.ts`): subscription metrics.

**Ads** (`ads.ts`): 15 endpoints under `/ads/*` — config, eligibility,
placements, impression/click tracking, ad units CRUD, campaigns CRUD, campaign
status, creatives, and analytics.

**Ad networks** (`ad-networks.ts`): ad-network configuration.

**B2B API** (`b2b-api.ts`): ~27 endpoints under `/b2b/*` — content feed
(`/b2b/feed`, `.../feed/trending`, `.../feed/:id`), search and suggest,
fact-check claims and search, entity search/CRUD/articles/relationships and
mention/relationship ingestion, media listing/search, alerts CRUD plus alert
notifications, and `/b2b/monitoring/stats`.

**API keys** (`api-keys.ts`): 11 endpoints under `/api-keys/*` — create, list,
detail, update, delete, rotate, per-key usage and billing, usage summary,
validation, and rate-limit checks.

_Grounding: `apps/veritas/api/src/interfaces/http/server.ts`;
`routes/v1/index.ts`; the individual route files under `routes/v1/`._

---

## 7. NLP Service (`veritas-nlp`)

`veritas-nlp` is a dedicated Hono microservice for all NLP analysis. It runs as
a separate process from the API so that CPU-intensive analysis tasks do not
block API response times. The NLP service exposes 23 HTTP endpoints (plus
health) organized around nine capability areas: sentiment, topics, keywords,
summarization, claims, language, embeddings, evidence, and Ghana-specific NLP.
Most analysis endpoints have a `/batch` variant for processing multiple items in
a single request.

The service is provider-agnostic: it routes each request to the best available
provider (e.g., Cohere for embeddings, HuggingFace for sentiment, Ghana NLP
Khaya for local-language tasks) and falls back gracefully if a provider is
unavailable. All provider API keys are read from environment at startup:

`HUGGINGFACE_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `COHERE_API_KEY`,
`SERPER_API_KEY`, `BRAVE_API_KEY`, `GHANA_NLP_API_KEY`, `KHAYA_API_KEY`.

### NLP Endpoints

The table below lists all 23 analysis endpoints plus the three health probes.
The default port is `NLP_PORT` (3002).

| Method | Path                      | Purpose                                   |
| ------ | ------------------------- | ----------------------------------------- |
| GET    | `/health`                 | Health check                              |
| GET    | `/healthz`                | Simple liveness                           |
| GET    | `/ready`                  | Readiness probe                           |
| POST   | `/sentiment`              | Sentiment analysis                        |
| POST   | `/sentiment/batch`        | Batch sentiment analysis                  |
| POST   | `/topics/classify`        | Topic / category classification           |
| POST   | `/topics/classify/batch`  | Batch topic classification                |
| POST   | `/keywords/extract`       | Keyword extraction                        |
| POST   | `/keywords/extract/batch` | Batch keyword extraction                  |
| POST   | `/summarize/extractive`   | Extractive summarization                  |
| POST   | `/summarize/abstractive`  | Abstractive (LLM) summarization           |
| POST   | `/claims/extract`         | Claim extraction from text                |
| POST   | `/claims/extract/batch`   | Batch claim extraction                    |
| POST   | `/language/detect`        | Language detection                        |
| POST   | `/language/code-switch`   | Code-switching detection                  |
| POST   | `/language/normalize`     | Ghanaian English normalization            |
| POST   | `/embeddings/embed`       | Text embedding generation                 |
| POST   | `/embeddings/embed/batch` | Batch embedding generation                |
| POST   | `/similarity/compare`     | Semantic similarity comparison            |
| POST   | `/evidence/search`        | Evidence retrieval for claim verification |
| POST   | `/ghana/translate`        | Ghana NLP cross-language translation      |
| POST   | `/ghana/tts`              | Ghana NLP local-language TTS              |
| POST   | `/ghana/ner`              | Named-entity recognition via Ghana NLP    |

### Service Providers

Each NLP capability is implemented under `src/services/` as a separate module
with a primary provider and optional fallbacks. The table below lists the
capability, its default provider, and any secondary providers.

The NLP service organizes implementations under `src/services/`, each with a
default provider plus fallbacks:

- **sentiment** — HuggingFace + a Ghana-calibration layer.
- **topics** — HuggingFace zero-shot classification.
- **keywords** — RAKE algorithm.
- **summarization** — extractive (statistical) and abstractive (LLM).
- **claims** — heuristic extractor plus an LLM extractor.
- **embeddings** — Cohere and OpenAI providers.
- **similarity** — HuggingFace.
- **evidence** — Serper and Brave search providers.
- **language** — detection and normalization.
- **ghana** — Ghana NLP / Khaya client (translate, TTS, NER).

_Grounding: `apps/veritas/nlp/src/server.ts`; `config.ts`; `src/services/*`
directory listing._

---

## 8. Ingestion Pipeline (`veritas-ingestion`)

`veritas-ingestion` is the content discovery engine. It runs as a CLI process
with four modes that together implement the full ingestion cycle. In production
these modes typically run as separate containers pointing at the same database
and Redis instance:

1. **Scheduler** polls the source registry on configured intervals and writes
   raw feed items to PostgreSQL.
2. **Producer** reads unprocessed feed items from PostgreSQL and pushes them
   onto the BullMQ queue for the Worker.
3. **Worker** picks up queue jobs, scrapes the full article, normalizes it, runs
   SimHash deduplication, and stores it in PostgreSQL and Elasticsearch.
4. **Seed** bootstraps a fresh installation with the 33 Tier-1 and government
   Ghana news sources.

A single CLI deployable with four modes (`apps/veritas/ingestion/src/main.ts`):

| Mode      | Command                  | Description                                         |
| --------- | ------------------------ | --------------------------------------------------- |
| Scheduler | `node main.js scheduler` | Fetches feeds on schedule, queues raw items         |
| Producer  | `node main.js producer`  | Enqueues unprocessed items into the BullMQ queue    |
| Worker    | `node main.js worker`    | Processes the queue: scrape, normalize, deduplicate |
| Seed      | `node main.js seed`      | Seeds the database with Ghana news sources          |

The seed command supports `--dry-run` and `--summary`.

### Collectors and Pipeline

The ingestion pipeline is composed of four sub-systems wired together in the
Worker mode. Collectors fetch raw feed data; the Scraper extracts the full
article body; the Pipeline normalizes and deduplicates; the Queue manages job
delivery and retry.

- **Collectors** (`src/collectors/`): RSS collector + parser, sitemap collector,
  social collector, X (Twitter) collector.
- **Scraper** (`src/scraper/`): article extraction, `robots.txt` handling,
  throttling.
- **Pipeline** (`src/pipeline/`): language detection, media extraction,
  normalization, timestamp normalization, SimHash near-duplicate detection, and
  the processing worker.
- **Queue** (`src/queue/`): the main processing queue is named
  `veritas:content-processing`; the per-item job type is `process_feed_item`.

### Seeded Sources

Running `node main.js seed` bootstraps the source registry with two sets of
Ghana news sources. New engineers should run the seed command after the first
database migration to get a working set of sources for local development.

The `seed` command upserts two source sets:

- **`TIER1_GHANA_SOURCES`** (`seeds/tier1-ghana-sources.ts`) — 21 Tier-1 Ghana
  media sources (e.g. MyJoyOnline, Adom Online, Citi Newsroom, Graphic Online),
  grouped by media group, contributing roughly 80 feeds in total.
- **`GOVERNMENT_SOURCES`** (`seeds/government-sources.ts`) — 12 Ghana government
  / institutional sources, contributing roughly 22 feeds.

Combined: 33 seeded sources / ~102 feeds. Helper functions (`getTotalFeedCount`,
`getMediaGroupSummary`, `getGovFeedSummary`, `getActiveGovSources`) summarize
the seed set; `runSeed` also prints a media- group breakdown and a bias-band
distribution.

_Grounding: `apps/veritas/ingestion/src/main.ts`;
`src/seeds/{seed, tier1-ghana-sources,government-sources,index}.ts`;
`src/queue/constants.ts`; `src/collectors/`, `src/pipeline/`, `src/scraper/`
directory listings._

---

## 9. AI Workers (`veritas-ai-workers`)

`veritas-ai-workers` is the background processing engine for AI enrichment. Each
worker is a BullMQ consumer that processes one specific job type — a clean
separation that allows individual workers to be scaled independently based on
queue depth. Workers are organized into four registries by function;
`ALL_WORKERS` merges all four for registration at startup.

To discover workers programmatically, use `listWorkers()` to get all 24 worker
definitions, or `getWorker(name)` to retrieve a specific one by its queue name.

BullMQ workers organized into four registries under
`apps/veritas/ai-workers/src/workers/`. `ALL_WORKERS` merges all four;
`getWorker` and `listWorkers` provide lookup. There are 24 workers in total.

**Content generation** (`CONTENT_WORKERS`, 8): `weather-report`,
`traffic-update`, `market-summary`, `fuel-price`, `gpl-score`,
`ecg-load-shedding`, `event-calendar`, `trend-analysis`.

**Content processing** (`PROCESSING_WORKERS`, 9): `article-summarization`,
`headline-variants`, `article-tagging`, `article-priority`, `claim-linking`,
`press-release`, `breaking-news`, `entity-extraction`, `seo-metadata`.

**Analysis** (`ANALYSIS_WORKERS`, 5): `story-clustering`, `source-accuracy`,
`blindspot-detection`, `political-bias`, `sentiment-analysis`.

**Editorial** (`EDITORIAL_WORKERS`, 2): `fact-check`, `original-content`.

Each worker exposes a `tick` function and a `getDefault*Options` factory.

_Grounding:
`apps/veritas/ai-workers/src/workers/{index,content/index, processing/index,analysis/index,editorial/index}.ts`._

---

## 10. Persistence — Prisma Schema

The Prisma schema at `libs/veritas/database/prisma/schema.prisma` (≈1,900 lines)
defines roughly 75 models and 50 enums. It is the authoritative source of truth
for the database structure — the domain object model in `@veritas/core` (§5)
mirrors it in TypeScript, but the Prisma schema is what actually migrates.

The schema is large; the sections below summarize the most important enums and
models organized by functional area. Engineers implementing new features should
read the full schema file directly, using this section as a map.

### 10.1 Selected Enums

The following enums are defined in the Prisma schema. Several have counterparts
in `@veritas/core` but with slightly different value sets — where there is a
difference, both are documented (see §5.4 for the `SourceKind` divergence).

`SourceStatus` (`active | paused | retired | pending`); `SourceKind`
(`newspaper | broadcaster | news_agency | digital_native | wire_service | publisher | government | ngo | blog | social | academic | other`
— broader than the `@veritas/core` `SOURCE_KINDS`); `OwnershipType`; `FeedType`;
`ArticleStatus`; `ArticleContentType`; `TranslationQuality`;
`PerspectiveStance`; `EntityType`; `MediaKind` / `MediaRole` / `MediaSource` /
`MediaStorageProvider`; `FactCheckOverallStatus`; `ClaimType`; `EvidenceStance`;
`ClusteringMethod`; `StoryClusterStatus`; `EmbeddingProvider`; `VoiceProvider`;
`SocialPlatform`; `ContentPriority`; `ScheduleRunStatus`;
`WhatsAppSubscriberStatus` / `WhatsAppPostStatus`; `SourcePreferenceType`;
`ContributorRole`; `CorrectionKind` / `CorrectionSeverity`; `AIReviewKind`; the
NMC enums (`NmcViolationCategory`, `NmcComplaintStage`, `NmcComplaintSeverity`,
`NmcComplainantType`, `NmcRemedy`); the DPA enums (`DpaLawfulBasis`,
`DpaProcessingPurpose`, `DpaRequestType`, `DpaRequestStatus`); the election
enums (`ElectionType`, `ElectionCoverageViolationType`,
`CoverageComplianceLevel`); and the payment enums (`PaymentTransactionType`,
`PaymentTransactionStatus`, `SubscriptionStatus`).

### 10.2 Selected Models by Area

The 75 models group naturally into the functional areas listed below. Each
bullet names the Prisma models that belong to that area; the relationships
between models in the same area are captured in the schema's `@relation`
directives.

- **Sources & feeds** — `Source`, `SourceFeed`, `FeedItem`,
  `NormalizedFeedItem`, `SocialMonitor`, `SocialPost`.
- **Articles** — `Article` (with `priority`, `canonicalNormalizedFeedItemId`,
  flags `isOriginal`/`isAIGenerated`/`humanReviewed`, engagement counters, and
  ~30 relations), `Author`, `ArticleAuthor`, `ArticleAttribution`,
  `ArticleTranslation`, `ArticlePerspective`, `ArticleRelated`.
- **Taxonomy** — `Category`, `Topic`, `Tag`, `ArticleCategory`, `ArticleTopic`,
  `ArticleTag`.
- **Entities** — `Entity`, `ArticleEntity`, `EntityRelationship`.
- **Media** — `MediaAsset`, `ArticleMedia`.
- **Story clusters** — `StoryCluster`, `StoryClusterMember`.
- **Fact-checking** — `Claim`, `ArticleClaim`, `ArticleFactCheck`,
  `ClaimVerification`, `ClaimEvidence`.
- **Embeddings** — `ArticleEmbedding` (pgvector).
- **Auth & users** — `AuthUser`, `AuthIdentity`, `PasswordReset`,
  `UserArticleRead`, `UserSavedArticle`, `UserTopicFollow`,
  `UserSourcePreference`, `UserAuthorFollow`.
- **Media production** — `VoiceProfile`, `ArticleAudioRender`, `AvatarProfile`,
  `ArticleVideoRender`.
- **Notifications** — `UserPushToken`, `PushNotificationOutbox`,
  `PushNotificationDelivery`.
- **Social scheduling** — `YouTubeShortsScheduleRun`, `TikTokScheduleRun`,
  `WhatsAppChannelSubscriber`, `WhatsAppChannelPost`.
- **Editorial** — `ArticleCorrection`, `ArticleAIReview`, `ArticleRevision`,
  `ArticleSummaryVariant`, `ArticleHeadlineVariant`, `CalendarEvent`.
- **Compliance** — `NmcComplaint`, `NmcComplaintTransition`, `NmcContentCheck`,
  `NmcViolationFlag`; `DpaConsent`, `DpaSubjectRequest`; `Election`,
  `ElectionCoverageViolation`.
- **Payments** — `PaymentTransaction`, `PaymentSubscription`.

_Grounding: `libs/veritas/database/prisma/schema.prisma` — model/enum
declarations._

---

## 11. Domain Events (`@veritas/events`)

The event bus enables loose coupling between Veritas services and other Oshun
domains. When an article is published, the `veritas.article.published` event
fires and any subscriber — inside or outside Veritas — can react without being
called directly. This is how, for example, a notification service learns that a
breaking news article has gone live without being tightly coupled to the CMS.

`@veritas/events` is built on `@oshun/event-bus`. It exposes
`createVeritasPublisher` / `createVeritasSubscriber` and the
`VERITAS_EVENT_TYPES` constant. The schema-to-payload mapping is
`VeritasEventPayloads`.

`VERITAS_EVENT_TYPES` defines 41 event type strings, all `veritas.*`-namespaced,
grouped into ten areas. The table below maps each constant name to its string
value and shows which group it belongs to.

| Group              | Event types (constant → string)                                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Article lifecycle  | `ARTICLE_CREATED` `veritas.article.created`; `ARTICLE_UPDATED`; `ARTICLE_PUBLISHED`; `ARTICLE_UNPUBLISHED`; `ARTICLE_ARCHIVED`; `ARTICLE_DELETED`                                                      |
| Content processing | `ARTICLE_INGESTED`; `ARTICLE_PROCESSED`; `ARTICLE_ENRICHED`; `ARTICLE_TRANSLATED`                                                                                                                      |
| Fact-checking      | `CLAIM_EXTRACTED`; `CLAIM_VERIFIED`; `FACT_CHECK_STARTED`; `FACT_CHECK_COMPLETED`; `FACT_CHECK_FAILED`                                                                                                 |
| Story clustering   | `STORY_CLUSTER_CREATED` `veritas.story.created`; `STORY_CLUSTER_UPDATED`; `STORY_CLUSTER_MERGED`; `ARTICLE_CLUSTERED`                                                                                  |
| Source & feed      | `SOURCE_CREATED`; `SOURCE_UPDATED`; `SOURCE_SUSPENDED`; `SOURCE_ACTIVATED`; `FEED_FETCHED`; `FEED_FAILED`                                                                                              |
| Media generation   | `VIDEO_GENERATION_STARTED`; `VIDEO_GENERATION_COMPLETED`; `VIDEO_GENERATION_FAILED`; `AUDIO_GENERATION_STARTED`; `AUDIO_GENERATION_COMPLETED`; `AUDIO_GENERATION_FAILED`; `IMAGE_GENERATION_COMPLETED` |
| Alerts             | `BREAKING_NEWS_PUBLISHED` `veritas.alert.breaking`; `MODERATION_ALERT`; `BIAS_ALERT`; `MISINFORMATION_ALERT`                                                                                           |
| Analytics          | `TRENDING_UPDATED`; `ENGAGEMENT_RECORDED`; `READER_MILESTONE`                                                                                                                                          |
| User               | `USER_SUBSCRIBED`; `USER_PREFERENCES_UPDATED`; `USER_SAVED_ARTICLE`                                                                                                                                    |
| NLP processing     | `NLP_SENTIMENT_COMPLETED`; `NLP_TOPICS_CLASSIFIED`; `NLP_KEYWORDS_EXTRACTED`; `NLP_SUMMARY_GENERATED`; `NLP_ENTITIES_EXTRACTED`                                                                        |

### Representative Event Payloads

Not all 41 event types have a dedicated payload interface — 37 do; the remaining
4 are declared as type constants only. The payloads below are the most commonly
subscribed-to events and illustrate the data available to consumers.

- **`ArticleCreatedPayload`** (extends `ArticleEventBase`: `articleId`, `slug`,
  `headline`, optional `sourceId`/`sourceName`/`authorIds`/`categoryIds`/
  `topicIds`, `languageTag`) — adds `contentType`, `isOriginal`,
  `isAIGenerated`, optional `sourceUrl`, `summary`.
- **`ClaimVerifiedPayload`** — `claimId`, optional `articleId`, `verdict`
  (`true | mostly_true | mixed | mostly_false | false | unverifiable`),
  `confidence`, an `evidence` array, optional `verifiedBy`, `verificationMethod`
  (`automated | manual | hybrid`).
- **`FeedFetchedPayload`** — `feedId`, `sourceId`, `sourceName`, `feedUrl`,
  `itemsFetched`, `newItems`, `fetchDurationMs`, optional `nextFetchAt`.
- **`BreakingNewsPublishedPayload`** — `alertId`, `articleId`, `headline`,
  optional `summary`, `priority` (`critical | high | normal`), optional
  `categoryIds`/`topicIds`, `expiresAt`.
- **`NLPEntitiesExtractedPayload`** — `articleId`, an `entities` array (`text`,
  `type`, `confidence`, `positions`, optional linked id/url),
  `processingDurationMs`.

Note: `VeritasEventPayloads` maps 37 of the 41 event types to explicit payload
types; `ARTICLE_ARCHIVED`, `ARTICLE_DELETED`, `SOURCE_SUSPENDED`,
`SOURCE_ACTIVATED`, `FACT_CHECK_FAILED`, the `*_GENERATION_STARTED`/`*_FAILED`
variants, and the three `USER_*` events are declared as type constants without a
dedicated payload interface.

_Grounding: `libs/veritas/events/src/types.ts`; `src/index.ts`._

---

## 12. Agent System (`veritas-agents`, `@veritas/agents-*`)

The agent system implements a virtual newsroom. Seven specialized agents run
concurrently, each with a defined role, priority, and set of capabilities. The
Orchestrator distributes incoming tasks to the appropriate agent; the Message
Bus handles inter-agent communication; the State Manager persists agent state
across restarts; and the Health Monitor self-heals failed agents.

For new engineers, the easiest mental model is: the 24 AI workers (§9) do fast,
queue-based enrichment without judgment; the 7 agents do slower, reasoned
editorial work with full editorial context and decision authority.

### 12.1 Agents Service

`veritas-agents` (`apps/veritas/agents/`) is a Hono service composed of:

- **Orchestrator** (`orchestration/orchestrator.ts`) — task distribution with
  `distributionStrategy: 'load_balanced'`, auto-scaling thresholds, agent
  registry, and periodic state sync.
- **Message Bus** (`communication/message-bus.ts`) — Redis pub/sub with an
  in-memory fallback, bounded queue, and retry policy.
- **State Manager** (`state/state-manager.ts`) — Redis KV state with a 24-hour
  TTL, compression, and the `veritas:state:` key prefix.
- **Health Monitor** (`monitoring/health-monitor.ts`) — self-healing with a
  failure threshold of 3, latency/error-rate thresholds, and a cap of 10 healing
  actions per hour.
- **Metrics Collector** (`metrics/metrics-collector.ts`) — per-agent metrics.

### 12.2 Default Agent Roster

`getDefaultAgents()` in `apps/veritas/agents/src/main.ts` registers the 7
default agents with the priorities and concurrency limits shown below. Higher
priority agents pre-empt lower-priority ones when resources are constrained. The
two journalist agents (IDs `journalist-1` and `journalist-2`) share the same
role constant but have different specializations (investigative and political
respectively).

`getDefaultAgents()` in `apps/veritas/agents/src/main.ts` registers **7
agents**:

| Agent ID                 | Role                         | Priority | Max concurrent tasks |
| ------------------------ | ---------------------------- | -------- | -------------------- |
| `editor-in-chief-1`      | `editor_in_chief`            | 10       | 5                    |
| `managing-editor-1`      | `managing_editor`            | 9        | 5                    |
| `fact-checker-1`         | `fact_checker`               | 8        | 3                    |
| `journalist-1`           | `journalist` (investigative) | 7        | 3                    |
| `journalist-2`           | `journalist` (political)     | 7        | 3                    |
| `content-strategist-1`   | `content_strategist`         | 6        | 3                    |
| `social-media-manager-1` | `social_media_manager`       | 5        | 5                    |

`AGENT_ROLES` (in `core/types.ts`) also defines system roles `orchestrator` and
`monitor`. `JOURNALIST_SPECIALIZATIONS` are
`political | business | sports | entertainment | regional | investigative | breaking_news | general`.
`AGENT_STATUSES` are
`initializing | idle | busy | paused | error | shutting_down | stopped`.
`TASK_TYPES` enumerates content-creation, verification, media, research,
editorial, and system task kinds.

### 12.3 Agents HTTP API

`veritas-agents` exposes an HTTP API for submitting tasks and monitoring agent
health. It is an internal API — only called by `veritas-api` and the CMS, not
exposed to external clients directly.

All routes prefixed `/v1`:

| Method | Path                          | Purpose                     |
| ------ | ----------------------------- | --------------------------- |
| GET    | `/health`, `/ready`           | Service liveness/readiness  |
| GET    | `/v1/agents`                  | List all agents             |
| GET    | `/v1/agents/:id`              | Agent detail                |
| GET    | `/v1/agents/role/:role`       | Agents filtered by role     |
| GET    | `/v1/agents/:id/health`       | Agent health                |
| GET    | `/v1/agents/:id/issues`       | Agent health issues         |
| POST   | `/v1/tasks`                   | Submit a task               |
| GET    | `/v1/tasks/:id`               | Task detail                 |
| GET    | `/v1/orchestrator/stats`      | Orchestrator statistics     |
| POST   | `/v1/orchestrator/distribute` | Trigger task distribution   |
| GET    | `/v1/health/issues`           | Active health issues        |
| GET    | `/v1/health/history`          | Self-healing history        |
| GET    | `/v1/metrics`                 | Metrics (JSON)              |
| GET    | `/v1/metrics/prometheus`      | Metrics (Prometheus format) |

### 12.4 Specialized Agent Libraries

`@veritas/agents-editorial` ships `editor-in-chief`, `managing-editor`, and
`content-strategist` (with a Google Trends provider).
`@veritas/agents- journalism` ships `investigative-correspondent`, `political`-,
`business`-, and `breaking-news-correspondent` agents.
`@veritas/agents-fact- checking` ships the `fact-checker` agent with
web-evidence search. `@veritas/agents-social-media`, `@veritas/agents-devops`,
`@veritas/agents-product`, and `@veritas/agents-qa` ship their respective
agents. `@veritas/agents-core` provides the base agent, LLM client,
context-window manager, message bus, state, health, metrics, and logging
primitives; `@veritas/agents-orchestrator` provides an orchestrator with a
registry, router, and conflict resolver.

_Grounding: `apps/veritas/agents/src/{main,server,core/types}.ts`;
`apps/veritas/agents/src/{orchestration,communication,state,monitoring, metrics}/`
directory listings; `libs/veritas/agents-*/src/` listings._

---

## 13. CMS (`veritas-cms`)

The CMS is the final gate before publication. Articles pass through it in a
`Draft → Review → Schedule → Publish` lifecycle. The AI review stage uses Claude
to produce a structured approve/reject/request-changes decision that either
progresses the article to scheduling or returns it to the journalist with
written rationale. The rate limiter and quiet-hours configuration prevent feed
flooding and respect Ghanaian audience patterns.

The CMS service (`apps/veritas/cms/`) runs the editorial workflow. Its
configuration (`src/config.ts`) sets:

- `maxArticlesPerWindow: 6` — at most 6 articles per 30-minute window.
- `defaultQuietHours: { start: '22:00', end: '06:00' }` — no scheduled
  publishing during quiet hours.
- `defaultTimezone: 'Africa/Accra'`.
- Optimal-time windows including a 06:00 morning slot and a 20:00–22:00 prime-
  time window.

Service modules: `articles/`, `revisions/` (revision service with diffing),
`reviews/` (AI-assisted review service), `scheduling/` (scheduler service with
quiet-hours enforcement), `queue/` (publication queue), and `storage/` (memory
storage adapter).

_Grounding: `apps/veritas/cms/src/{config,types,main}.ts`; `src/` directory
listing._

---

## 14. Content Authentication (`@veritas/content-auth`)

Content authentication gives Veritas's AI-generated articles and media assets a
cryptographic provenance record that can be independently verified. This matters
for two reasons: (1) it allows Veritas to prove an article's origin if it is
later disputed or misattributed; (2) the C2PA manifests make the content
interoperable with other publishers and platforms implementing the C2PA standard
for AI-generated media disclosure.

`@veritas/content-auth` implements cryptographic content hashing, blockchain
timestamp proofs, authenticity certificates, media-origin tracking, edit
history, and device signing, plus deepfake and image-manipulation detection. It
exports managers including `BlockchainTimestampManager` and
`DeviceSigningManager` (with `create*` factory functions).

The type surface includes `ContentHash` / `HashAlgorithm`, `BlockchainTimestamp`
/ `BlockchainNetwork` / `MerkleTreeNode` / `BatchTimestamp`,
`AuthenticityCertificate` / `DigitalSignature` / `VerificationBadge` /
`CustodyRecord`, `MediaOrigin` / `ExifData`, and a full **C2PA** type set —
`C2PAManifest`, `C2PAAssertion`, `C2PACredential`, `C2PASignature`,
`C2PAValidationStatus` — for content-provenance manifests.

_Grounding: `libs/veritas/content-auth/src/index.ts`; `src/` listing._

---

## 15. V2 Esports Fact-Checking Bridge

The V2 esports tooling package `@v2/esports-tools` (`apps/v2/esports-tools/`)
consumes `@veritas/fact-checking` to verify esports outcomes and reporting after
matches conclude. Veritas owns the verification primitives; V2 owns only the
esports framing and the rollback isolation. Veritas itself does not depend on
V2.

`veritas-esports-fact-checking.ts` exposes `buildV2VeritasEsportsFactChecking`
(binding id `v2.esports.veritas-fact-checking`), which composes the real Veritas
scoring primitives — `calculateVerificationScore`, `calculateConsensus`, and
`getDomainCredibility` — over evidence collected for three surfaces: **bracket
result verification**, **post-match reporting**, and the **esports news**
pipeline. The output is Veritas's own `FactCheckReport` type, so a bracket or
news claim is scored against weighted, domain-credibility-ranked evidence
exactly as any other Veritas claim would be.

The contract is deliberately advisory and **off rollback**. Its rollback policy
is `off-rollback-post-match-journalism` and it carries
`mayInfluenceRollback: false`: a fact-check verdict can never rewrite a match
that already happened, reorder a bracket, or feed back into the deterministic
simulation. A verified bracket emits
`v2.esports.veritas.bracket-result.verified`. When the reported winner, the
replay winner, and the bracket winner disagree, the bridge does not silently
pick one — it holds publication of the affected post-match report or news story
until the conflict is resolved by an editor, so unverified esports claims never
ship as fact.

This bridge is documented under
`V2/docs/integration/veritas-esports-fact-checking.md` and is verified by
`V2/ue/Tools/check-v2-veritas-esports-fact-checking.py`.

_Grounding: `apps/v2/esports-tools/src/veritas-esports-fact-checking.ts`;
`libs/veritas/fact-checking/src/`._

---

## 16. External Integrations

The table below maps each external service to the library or application that
uses it and summarizes its purpose. All provider credentials are injected as
environment variables — no keys are hard-coded in source.

| Service           | Library/App                               | Purpose                                      |
| ----------------- | ----------------------------------------- | -------------------------------------------- |
| Anthropic Claude  | `llm`, `agents-core`, nlp                 | LLM orchestration, abstractive summarization |
| OpenAI            | `llm`, nlp                                | Alternative LLM provider, embeddings         |
| Cohere            | nlp (`embeddings`)                        | Embeddings                                   |
| HuggingFace       | nlp (`sentiment`, `topics`, `similarity`) | Inference API                                |
| Ghana NLP (Khaya) | `ghana-nlp`, nlp (`ghana`)                | Local-language TTS, NER, translation         |
| Serper            | nlp (`evidence`)                          | Search-based evidence retrieval              |
| Brave Search      | nlp (`evidence`)                          | Alternative evidence search                  |
| HeyGen            | `video-production`, `veritas-video`       | AI video avatar rendering                    |
| ElevenLabs        | `audio-production`, `veritas-audio`       | English text-to-speech                       |
| Stripe            | `payments`, `veritas-api`                 | Card payments and subscriptions              |
| Mobile money      | `payments`, `veritas-api`                 | MTN MoMo, Vodafone Cash, AirtelTigo Money    |
| Firebase (FCM)    | `notifications`                           | Push notifications                           |
| Social platforms  | `social-automation`, `veritas-social`     | YouTube, TikTok, Meta, Telegram, etc.        |

_Grounding: `apps/veritas/nlp/src/config.ts`; route files under
`apps/veritas/api/src/interfaces/http/routes/v1/`; library directory names._

---

## 17. Configuration Reference

The table below lists the environment variables required by the main Veritas
services. Variables not listed here (e.g., social platform API keys, HeyGen
credentials) follow the same pattern: read from environment, no defaults in
source code.

| Variable               | Consumed by              | Purpose                         |
| ---------------------- | ------------------------ | ------------------------------- |
| `VERITAS_DATABASE_URL` | database, api, ingestion | PostgreSQL connection string    |
| `PORT`                 | api, video, social       | HTTP port (api default 3002)    |
| `NLP_PORT`             | nlp                      | NLP service port (default 3002) |
| `HUGGINGFACE_API_KEY`  | nlp                      | HuggingFace inference           |
| `ANTHROPIC_API_KEY`    | nlp, agents, ai-workers  | Claude API access               |
| `OPENAI_API_KEY`       | nlp, agents              | OpenAI access                   |
| `COHERE_API_KEY`       | nlp                      | Cohere embeddings               |
| `SERPER_API_KEY`       | nlp                      | Serper evidence search          |
| `BRAVE_API_KEY`        | nlp                      | Brave evidence search           |
| `GHANA_NLP_API_KEY`    | nlp                      | Ghana NLP / Khaya API           |
| `KHAYA_API_KEY`        | nlp                      | Khaya API (alternate key)       |

_Grounding: `apps/veritas/api/src/main.ts`; `apps/veritas/nlp/src/config.ts`._

---

## 18. Acceptance Criteria

The acceptance criteria below define the minimum observable behaviors that
confirm a Veritas deployment is correctly wired. They can be used as a
post-deployment smoke test checklist and as the reference for what a CI
integration test suite should verify.

A deployment of the Veritas domain is correct when:

1. **Type integrity** — `@veritas/core` exports the documented branded
   primitives, entities, enums, and error classes; `@veritas/models` Zod schemas
   validate them.
2. **API surface** — `veritas-api` mounts all v1 route groups under `/api/v1`,
   serves OpenAPI at `/openapi.json` and Swagger UI at `/docs`, and applies the
   middleware chain (timing, security headers, rate limiting, sandbox detection,
   API-key auth) in order.
3. **NLP service** — `veritas-nlp` exposes the documented analysis endpoints and
   reports readiness via `/ready`.
4. **Ingestion** — `veritas-ingestion` runs in scheduler, producer, worker, and
   seed modes; `seed` upserts the 33 Tier-1 and government Ghana sources.
5. **AI workers** — the 24 workers across the four registries are discoverable
   via `listWorkers` and `getWorker`.
6. **Persistence** — the Prisma schema migrates against PostgreSQL with the
   `pgvector` and `citext` extensions.
7. **Events** — `@veritas/events` publishes and subscribes the 41 `veritas.*`
   event types via `@oshun/event-bus`.
8. **Agents** — `veritas-agents` registers the 7-agent roster with the
   documented priorities and exposes the `/v1/*` orchestration API.
9. **CMS** — `veritas-cms` enforces the 6-articles-per-30-minutes rate limit and
   the 22:00–06:00 Africa/Accra quiet hours.

---

## Grounding Statement

Every entity, field, enum value, endpoint, event, and count in this document was
read directly from source in `libs/veritas/*` and `apps/veritas/*` — principally
`libs/veritas/core/src/`, `libs/veritas/events/src/types.ts`,
`libs/veritas/database/prisma/schema.prisma`, and the route, worker, seed,
agent, and config files cited under each section's grounding note. No counts,
schemas, or integrations were inferred. Where the codebase and an earlier draft
of these docs disagreed (source counts, the API route prefix, the language-tag
set, the worker roster), the code was treated as authoritative.
