# hesperine/herald architecture

> Architecture diagram of the GitHub repository hesperine/herald, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-28). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/hesperine/herald
- Repository: https://github.com/hesperine/herald

## Overview

HERALD scans official game-community accounts, turns public posts into deduplicated candidate notices and structured Campaign → Activity → Action facts, then schedules reminders and publishes a static activity site. The README documents the daily scan, pending-extraction retries, digest rules, seven-day reminder snapshots, and optional reachability and media features. The graph uses those documented workflows alongside the sampled source and storage implementations; orchestration details not shown in excerpts are represented only where the README supports them.

Main entry points: [`src/herald/runner.py`](https://github.com/hesperine/herald/blob/main/src/herald/runner.py) (Run orchestration), [`src/herald/pipeline.py`](https://github.com/hesperine/herald/blob/main/src/herald/pipeline.py) (Observation pipeline), [`src/herald/semantic_pipeline.py`](https://github.com/hesperine/herald/blob/main/src/herald/semantic_pipeline.py) (Semantic processing), [`src/herald/site.py`](https://github.com/hesperine/herald/blob/main/src/herald/site.py) (Static site builder)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_collection["Collection"]
  node_runner["Run orchestration<br/>[runner.py]"]
  node_config["Runtime settings<br/>[config.py]"]
  node_registry["Source registry<br/>[registry.py]"]
  node_miyoushe["Miyoushe adapter<br/>[miyoushe.py]"]
  node_skland["Skland adapter<br/>[skland.py]"]
  node_weibo["Weibo adapter<br/>[weibo.py]"]
end

subgraph group_processing["Extraction and facts"]
  node_pipeline["Observation pipeline<br/>[pipeline.py]"]
  node_dedupe["Source deduplication<br/>[dedupe.py]"]
  node_candidate["Candidate notices"]
  node_ai["AI extraction<br/>[ai.py]"]
  node_semantic["Semantic processing"]
  node_assembly["Campaign assembly<br/>[assembly.py]"]
  node_merge["Campaign merging<br/>[merge.py]"]
  node_models["Domain facts<br/>[models.py]"]
end

subgraph group_state["State and reminders"]
  node_storage[("Filesystem state<br/>[storage.py]")]
  node_scheduler["Date queue<br/>[scheduler.py]"]
end

subgraph group_delivery["Publishing and delivery"]
  node_notifications["Digest and reminders<br/>[notifications.py]"]
  node_site["Static site builder<br/>[site.py]"]
  node_media["Image caching<br/>[media.py]"]
  node_reachability["Reachability labels<br/>[reachability.py]"]
end

node_actions(("Daily run"))
node_communities(("Official communities"))
node_ai_service(("Configured AI service"))
node_smtp(("User SMTP"))
node_pages(("Published site"))

node_actions -->|"starts run"| node_runner
node_runner -->|"loads settings"| node_config
node_runner -->|"resolves sources"| node_registry
node_registry -->|"selects adapter"| node_miyoushe
node_registry -->|"selects adapter"| node_skland
node_registry -.->|"optional adapter"| node_weibo
node_miyoushe -->|"reads timelines"| node_communities
node_skland -->|"reads timelines"| node_communities
node_weibo -.->|"reads timelines"| node_communities
node_runner -->|"processes observations"| node_pipeline
node_pipeline -->|"deduplicates"| node_dedupe
node_pipeline -->|"registers candidates"| node_candidate
node_pipeline -->|"extracts facts"| node_semantic
node_semantic -->|"requests extraction"| node_ai
node_ai -.->|"sends public material"| node_ai_service
node_semantic -->|"assembles facts"| node_assembly
node_semantic -->|"merges revisions"| node_merge
node_assembly -->|"creates domain facts"| node_models
node_merge -->|"updates domain facts"| node_models
node_pipeline -->|"saves observations"| node_storage
node_semantic -->|"reads and writes state"| node_storage
node_runner -->|"compiles reminders"| node_scheduler
node_scheduler -->|"stores date queue"| node_storage
node_runner -->|"prepares digest"| node_notifications
node_notifications -->|"reads due work and records receipts"| node_storage
node_notifications -->|"sends digest"| node_smtp
node_runner -->|"builds publication"| node_site
node_site -->|"reads facts and snapshots reminders"| node_storage
node_site -->|"caches visible images"| node_media
node_site -->|"classifies locations"| node_reachability
node_site -->|"writes static files"| node_pages

click node_runner "https://github.com/hesperine/herald/blob/main/src/herald/runner.py"
click node_config "https://github.com/hesperine/herald/blob/main/src/herald/config.py"
click node_registry "https://github.com/hesperine/herald/blob/main/src/herald/registry.py"
click node_miyoushe "https://github.com/hesperine/herald/blob/main/src/herald/sources/miyoushe.py"
click node_skland "https://github.com/hesperine/herald/blob/main/src/herald/sources/skland.py"
click node_weibo "https://github.com/hesperine/herald/blob/main/src/herald/sources/weibo.py"
click node_pipeline "https://github.com/hesperine/herald/blob/main/src/herald/pipeline.py"
click node_dedupe "https://github.com/hesperine/herald/blob/main/src/herald/dedupe.py"
click node_candidate "https://github.com/hesperine/herald/blob/main/src/herald/candidate_notices.py"
click node_ai "https://github.com/hesperine/herald/blob/main/src/herald/ai.py"
click node_semantic "https://github.com/hesperine/herald/blob/main/src/herald/semantic_pipeline.py"
click node_assembly "https://github.com/hesperine/herald/blob/main/src/herald/assembly.py"
click node_merge "https://github.com/hesperine/herald/blob/main/src/herald/merge.py"
click node_models "https://github.com/hesperine/herald/blob/main/src/herald/models.py"
click node_storage "https://github.com/hesperine/herald/blob/main/src/herald/storage.py"
click node_scheduler "https://github.com/hesperine/herald/blob/main/src/herald/scheduler.py"
click node_notifications "https://github.com/hesperine/herald/blob/main/src/herald/notifications.py"
click node_site "https://github.com/hesperine/herald/blob/main/src/herald/site.py"
click node_media "https://github.com/hesperine/herald/blob/main/src/herald/media.py"
click node_reachability "https://github.com/hesperine/herald/blob/main/src/herald/reachability.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_runner,node_config,node_registry,node_miyoushe,node_skland,node_weibo,node_smtp toneBlue
class node_pipeline,node_dedupe,node_candidate,node_ai,node_semantic,node_assembly,node_merge,node_models toneAmber
class node_storage,node_scheduler toneMint
class node_notifications,node_site,node_media,node_reachability toneRose
class node_actions,node_communities,node_ai_service,node_pages toneIndigo
```

## Components

### Collection

- [Run orchestration](https://github.com/hesperine/herald/blob/main/src/herald/runner.py): `src/herald/runner.py`
- [Runtime settings](https://github.com/hesperine/herald/blob/main/src/herald/config.py): `src/herald/config.py`
- [Source registry](https://github.com/hesperine/herald/blob/main/src/herald/registry.py): `src/herald/registry.py`
- [Miyoushe adapter](https://github.com/hesperine/herald/blob/main/src/herald/sources/miyoushe.py): `src/herald/sources/miyoushe.py`
- [Skland adapter](https://github.com/hesperine/herald/blob/main/src/herald/sources/skland.py): `src/herald/sources/skland.py`
- [Weibo adapter](https://github.com/hesperine/herald/blob/main/src/herald/sources/weibo.py): `src/herald/sources/weibo.py`

### Extraction and facts

- [Observation pipeline](https://github.com/hesperine/herald/blob/main/src/herald/pipeline.py): `src/herald/pipeline.py`
- [Source deduplication](https://github.com/hesperine/herald/blob/main/src/herald/dedupe.py): `src/herald/dedupe.py`
- [Candidate notices](https://github.com/hesperine/herald/blob/main/src/herald/candidate_notices.py): `src/herald/candidate_notices.py`
- [AI extraction](https://github.com/hesperine/herald/blob/main/src/herald/ai.py): `src/herald/ai.py`
- [Semantic processing](https://github.com/hesperine/herald/blob/main/src/herald/semantic_pipeline.py): `src/herald/semantic_pipeline.py`
- [Campaign assembly](https://github.com/hesperine/herald/blob/main/src/herald/assembly.py): `src/herald/assembly.py`
- [Campaign merging](https://github.com/hesperine/herald/blob/main/src/herald/merge.py): `src/herald/merge.py`
- [Domain facts](https://github.com/hesperine/herald/blob/main/src/herald/models.py): `src/herald/models.py`

### State and reminders

- [Filesystem state](https://github.com/hesperine/herald/blob/main/src/herald/storage.py): `src/herald/storage.py`
- [Date queue](https://github.com/hesperine/herald/blob/main/src/herald/scheduler.py): `src/herald/scheduler.py`

### Publishing and delivery

- [Digest and reminders](https://github.com/hesperine/herald/blob/main/src/herald/notifications.py): `src/herald/notifications.py`
- [Static site builder](https://github.com/hesperine/herald/blob/main/src/herald/site.py): `src/herald/site.py`
- [Image caching](https://github.com/hesperine/herald/blob/main/src/herald/media.py): `src/herald/media.py`
- [Reachability labels](https://github.com/hesperine/herald/blob/main/src/herald/reachability.py): `src/herald/reachability.py`

### Other components

- **Daily run**
- **Official communities**
- **Configured AI service**
- **User SMTP**
- **Published site**

## Connections

- Daily run → Run orchestration: starts run
- Run orchestration → Runtime settings: loads settings
- Run orchestration → Source registry: resolves sources
- Source registry → Miyoushe adapter: selects adapter
- Source registry → Skland adapter: selects adapter
- Source registry → Weibo adapter: optional adapter
- Miyoushe adapter → Official communities: reads timelines
- Skland adapter → Official communities: reads timelines
- Weibo adapter → Official communities: reads timelines
- Run orchestration → Observation pipeline: processes observations
- Observation pipeline → Source deduplication: deduplicates
- Observation pipeline → Candidate notices: registers candidates
- Observation pipeline → Semantic processing: extracts facts
- Semantic processing → AI extraction: requests extraction
- AI extraction → Configured AI service: sends public material
- Semantic processing → Campaign assembly: assembles facts
- Semantic processing → Campaign merging: merges revisions
- Campaign assembly → Domain facts: creates domain facts
- Campaign merging → Domain facts: updates domain facts
- Observation pipeline → Filesystem state: saves observations
- Semantic processing → Filesystem state: reads and writes state
- Run orchestration → Date queue: compiles reminders
- Date queue → Filesystem state: stores date queue
- Run orchestration → Digest and reminders: prepares digest
- Digest and reminders → Filesystem state: reads due work and records receipts
- Digest and reminders → User SMTP: sends digest
- Run orchestration → Static site builder: builds publication
- Static site builder → Filesystem state: reads facts and snapshots reminders
- Static site builder → Image caching: caches visible images
- Static site builder → Reachability labels: classifies locations
- Static site builder → Published site: writes static files
