Перейти к содержанию

Seams — which module writes which table🔗

Curated (the Evergreen layer of Living Documentation), derived from src/polit_media_radar/persistence/repositories.py plus a grep of direct (non-repository) writers. Unlike modules.md and the ER diagrams under db/, this map is not regenerated by a single tool — when the persistence layer changes, re-run tools/extract_seams.py and re-check this file.

A seam is the point where a module persists state into a table, almost always through a repository class in the persistence layer. The diagram shows the backbone seams; the table below is the exhaustive list. Convention:

  • solid arrow = writer (the module inserts/updates the table through the repository)
  • dashed arrow = reader / FK lookup
  • dotted arrow + blue table = direct write that bypasses the repository layer (an architectural smell worth knowing)
  • grey dashed table = writer pending: the repository exists as a skeleton (raise NotImplementedError), the table is in the schema, but nothing writes it yet
  • yellow table = the candidates/fetch_runs spine shared by both contexts
flowchart LR
  classDef spine fill:#ffe,stroke:#b80,stroke-width:3px
  classDef skeleton stroke-dasharray:5 5,fill:#eee,color:#888
  classDef direct fill:#eef,stroke:#66c

  ingestion["ingestion"]
  cli["cli"]
  services["services"]
  reports["reports"]

  subgraph radar["legacy context (radar.*)"]
    candidates["candidates"]:::spine
    fetch_runs["fetch_runs"]:::spine
    articles["articles"]
    comments["comments"]
    article_restrictions["article_restrictions<br/>(no writer)"]:::skeleton
  end

  subgraph radar_ss["SocialScan context (radar.ss_*)"]
    ss_snapshots["ss_snapshots"]
    ss_posts["ss_posts"]
    ss_ai_reports["ss_ai_reports"]
    ss_trends_daily["ss_trends_daily"]
    ss_themes["ss_themes"]:::skeleton
    ss_wave_runs["ss_wave_runs"]:::skeleton
    ss_post_tonality["ss_post_tonality"]:::direct
    weekly_corpus_summary["weekly_corpus_summary"]:::direct
  end

  ingestion -->|ArticleRepository| articles
  ingestion -->|CandidatesRepository| candidates
  ingestion -->|FetchRunsRepository| fetch_runs
  ingestion -->|CommentsRepository| comments
  ingestion -->|SsSnapshotsRepository| ss_snapshots
  ingestion -->|SsPostsRepository| ss_posts
  ingestion -->|SsTrendsDailyRepository| ss_trends_daily
  cli -->|SsAiReportsRepository| ss_ai_reports
  ss_snapshots -.->|FK| fetch_runs
  ss_posts -.->|FK| candidates
  reports -.->|reads| ss_ai_reports
  cli -.->|direct write, no repo| ss_post_tonality
  services -.->|direct write, no repo| weekly_corpus_summary

  subgraph legend["Legend"]
    L1["module"] -->|writer| L2["table"]
    L3["module"] -.->|reader / FK| L4["table"]
    L5["writer pending"]:::skeleton
    L6["direct write (no repo)"]:::direct
    L7["spine"]:::spine
  end

Exhaustive repository → table map🔗

One row per repository in repositories.py. "Reads" lists secondary tables the repository touches for FK lookups / joins. ⚠️ = writer-pending skeleton (NotImplementedError).

Legacy context (radar.*)🔗

Repository Writes Reads
ArticleRepository articles article_revisions
CandidatesRepository candidates
FetchRunsRepository fetch_runs
MetricsSnapshotRepository article_metrics_snapshots
CommentsRepository comments
CommentAuthorsRepository comment_authors mv_comment_authors_stats (view)
ReactionSnapshotsRepository reaction_snapshots
ForwardsRepository forwards
ChannelSnapshotsRepository channel_snapshots
ArticleRevisionsRepository article_revisions
ArticleEntitiesRepository article_hashtags, article_mentions
(no repository) article_restrictionsno writer found (schema present, unused)

SocialScan context (radar.ss_* + weekly_corpus_summary)🔗

Repository Writes Reads
SsSnapshotsRepository ss_snapshots ss_district_summaries
SsPostsRepository ss_posts
SsPostClassificationsRepository ss_post_classifications
SsThemesRepository ss_themes ⚠️
SsRecommendationsRepository ss_recommendations ⚠️
SsJudgeResultsRepository ss_judge_results ⚠️
SsWaveRunsRepository ss_wave_runs ⚠️
SsDistrictSummariesRepository ss_district_summaries
SsTrendsRepository ss_trends
SsTrendsDailyRepository ss_trends_daily (raw SQL, no ORM model)
SsAiReportsRepository ss_ai_reports
SsOwnPostsRepository ss_own_posts candidates, ss_snapshots
SsNetworkPostsRepository ss_network_posts ss_snapshots
SsNarodnayaVerdictsRepository ss_narodnaya_verdicts ss_snapshots
SsSanityCorrectionsRepository ss_sanity_corrections ss_narodnaya_verdicts
SsPostEventsRepository ss_post_events ss_snapshots
SsChannelArchetypesRepository ss_channel_archetypes
SsMentionRelevanceRepository ss_mention_relevance
SsChannelFollowersWeeklyRepository ss_channel_followers_weekly

Writes that bypass the repository layer🔗

These tables are written directly from a module, not through a repository — a deviation from the clean module → repository → table pattern, worth tracking:

Table Written by Note
ss_post_tonality cli/tonality.py tonality metric backfill writes raw, no repo — accepted exception (ADR-0018)
weekly_corpus_summary services/weekly_corpus_summary.py service writes directly — accepted exception (ADR-0018)

Derived from repositories.py (via tools/extract_seams.py) + grep for direct writers. SC-002's automated drift gate covers only the tool-generated diagrams (modules, db/); this curated map must be re-verified by hand when the persistence layer changes.