# Source of Truth — Методологія моніторингу мови ворожнечі щодо ЛГБТІК+
## Єдиний робочий файл для Claude Code

> **Замінює:** `migration-plan.md` та `claude-code-source-methodology-monitoring.md`
> **Офіційна методологія:** Бюро "Ми — є!", Додаток I + Додаток II (CoE Follow-Up Report, 17.03.2026)

---

## 0. Призначення файлу

Це єдиний source of truth для трьох задач:

1. **Узгодження бази даних** з методологією (additive changes)
2. **Публічна сторінка методології** у розділі Аналітика
3. **Адаптація `analityka/post/index.html`** для методологічних long-form сторінок

Якщо є конфлікт між старим кодом і методологією — адаптувати код, не спотворювати методологію.

---

## 1. Пріоритет джерел для Claude Code

1. Методологія / codebook (офіційний документ Бюро)
2. Поточна архітектура сайту і Supabase-схема
3. Поточний HTML monitor/index.html — стилістична сумісність
4. Попередні SEO-напрацювання — структура сторінки, не зміст методології

---

## 2. Непорушні правила

### 2.1. Мовні та інституційні
- Завжди: **ЛГБТІК+**, **проєкт**, **Бюро "Ми — є!"**
- Не приписувати авторам контенту намірів
- У публічній версії не відтворювати дослівні приклади мови ворожнечі
- Кодування — аналітична класифікація, не правова кваліфікація

### 2.2. Безпека
- `config.js` не чіпати, не комітити
- `internal_notes` ніколи публічно
- Не виносити персональні дані в HTML, API, JSON-LD, OG
- HS6/doxxing/outing: зберігати лише факт ризику (`doxxing_risk` маркер), не самі дані

---

## 3. Поточний стан DB — що вже існує

### 3.1. Таблиця `records` (вже є)

Поля, що стосуються content stream та harm & risk stream:

| Поле | Тип |
|------|-----|
| `record_id` | text |
| `status` | text, CHECK 7 статусів |
| `date` | date |
| `platform` | text, CHECK |
| `source_type` | text, CHECK |
| `outlet` | text |
| `content_format` | text, CHECK |
| `url` | text |
| `label` | text |
| `language` | text |
| `target_group` | text |
| `link_status` | text, CHECK |
| `last_checked_at` | date |
| `narrative_code` | text, CHECK N1–N10 |
| `hate_codes` | text[] |
| `d_codes` | text[] |
| `severity` | smallint 0–4 |
| `harm_flags` | text[] |
| `confidence` | text, CHECK low/medium/high |
| `excerpt_neutral` | text |
| `archived_url` | text |
| `archived_at` | date |
| `archive_service` | text, CHECK wayback/archive_today/local_pdf/other |
| `ai_assisted` | boolean |
| `ai_model` | text |
| `ai_suggested_codes` | jsonb |
| `ai_codes_accepted` | boolean |
| `coded_by` | uuid |
| `coded_at` | timestamptz |
| `verified_by` | uuid |
| `verified_at` | timestamptz |
| `published_at` | timestamptz |
| `internal_notes` | text |
| `region` | text | Область/регіон матеріалу; є в production, відсутнє в initial_schema.sql — синхронізується migration 010 |
| `created_at` / `updated_at` | timestamptz |

### 3.2. Таблиця `response_actions` (ВЖЕ ІСНУЄ — migration 20260420105216_initial_schema.sql)

**КРИТИЧНО:** `migration-plan.md` (застарілий) пропонував додати response поля inline до `records`. Це НЕПРАВИЛЬНО. Окрема таблиця вже існує і є методологічно кращою (one-to-many: один запис може мати кілька реагувань).

| Поле | Тип | Примітки |
|------|-----|---------|
| `id` | uuid PK | |
| `record_id` | uuid FK → records.id | CASCADE DELETE |
| `response_status` | text, NOT NULL, DEFAULT 'no_data' | CHECK 5 значень |
| `responder_name_ua` | text | Офіційна назва українською |
| `responder_type` | text | CHECK 10 типів |
| `responder_level` | text | CHECK local/national/international/platform |
| `response_action` | text | CHECK RA1–RA6 або NULL |
| `first_response_date` | date | |
| `last_response_date` | date | |
| `lag_days` | integer, **GENERATED ALWAYS AS STORED** | автоматично: first_response_date − records.date |
| `response_url` | text | |
| `response_archived_url` | text | |
| `response_evidence_type` | text | CHECK 6 значень |
| `verification_level` | text, DEFAULT 'mixed' | CHECK mixed/strict |
| `created_at` / `updated_at` | timestamptz | |

RLS ввімкнено. Тригер `response_actions_updated_at` є.

### 3.3. Наявні тригери на `records`

- `records_updated_at` — BEFORE UPDATE, оновлює `updated_at`
- `records_status_timestamps` — BEFORE UPDATE ON status, автоматично заповнює `coded_at`, `verified_at`, `published_at`
- `audit_records` — AFTER INSERT/UPDATE/DELETE, пише в `audit_log`

---

## 4. Що залишилось зробити з DB

### 4.1. DB-фікси (Migration 010)

```sql
-- 010_records_region_and_lag_check.sql

-- Документування region (вже існує в Supabase через Dashboard, ADD IF NOT EXISTS безпечно)
ALTER TABLE public.records
  ADD COLUMN IF NOT EXISTS region text;

-- lag_days не може бути від'ємним
ALTER TABLE public.response_actions
  ADD CONSTRAINT chk_lag_days_non_negative
  CHECK (lag_days IS NULL OR lag_days >= 0);
```

**`region` в `records`:**
Фіксує географічний регіон (область) матеріалу або джерела. Корисне для:
- розбивки по регіонах на публічному дашборді
- фільтрації в адмін-панелі
- аналітики кластеризації severity 3–4 по регіонах
- звітів і адвокації

> ⚠️ Поле вже існує в production Supabase (додано вручну через Dashboard), але відсутнє в `initial_schema.sql`. Migration 010 синхронізує стан файлів з реальністю через `ADD COLUMN IF NOT EXISTS`.

**`lag_days >= 0`:** захист від помилкового введення дат (first_response_date < records.date).

> ⚠️ Нумерація: `009_posts_add_transparency_policy.sql` вже існує. Наступна міграція — **010**.

### 4.2. Що НЕ треба робити з DB

- **Не** додавати response поля inline до `records` — вони вже є в `response_actions`
- **Не** створювати тригер для `lag_days` — він вже `GENERATED ALWAYS`
- **Не** створювати нову таблицю реагувань — вона вже є
- **Не** змінювати `url`, `outlet`, `platform`, `severity`, `archive_service`, `source_type`
- **Не** повертати застарілі `severity_level`, `source_url`, `source_name`, `hate_speech_type`

### 4.3. Відкладені таблиці (Phase 2+)

Ці таблиці корисні, але не блокують запуск:

**`source_registry`** — паспорти джерел моніторингової панелі:
- `source_id`, `category` (A/B/C/D/E/F), `name/outlet`, `access_type`, `content_format`
- `inclusion_date`, `monitoring_frequency`, `archiving_feasible`, `notes`

**`methodology_change_log`** — журнал змін схеми/кодбуку:
- `changed_at`, `object_type`, `change_summary`, `reason`, `comparability_impact`, `changed_by_role`

**`methodology_versions`** — явне версіонування:
- `id`, `version`, `effective_date`, `notes`

---

## 5. Методологія — кодування

### 5.1. HS-коди (HS1–HS7)

| Код | Тип патерну | Операційна ознака |
|-----|-------------|-------------------|
| HS1 | Дегуманізаційне / знеособлювальне фреймування | Перехід від критики ідей до заперечення гідності групи |
| HS2 | Криміналізувальне / небезпечнісне рамкування | Кримінальні ярлики як делегітимізація без фактичної опори |
| HS3 | Сек'юритизаційне рамкування | Перенесення теми з прав у сферу безпеки |
| HS4 | Вимоги обмеження прав / інституційного виключення | «не повинні мати / отримувати / допускатися» |
| HS5 | Спонукання до фізичної шкоди / самосуду | Мовні конструкції, що можуть сприйматися як спонукання до дій |
| HS6 | Розкриття персональних / ідентифікаційних даних | Наявність інформації, що полегшує цільове переслідування |
| HS7 | Апеляція до свободи вираження як легітимація | Подання вимог виключення як «раціональної позиції» |

**Правило:** допускається множинне кодування. При сумніві — код домінантного механізму фреймування, не загальна тональність.

### 5.2. D-коди (D1–D7)

| Код | Тип | Операційна ознака |
|-----|-----|-------------------|
| D1 | Фабрикація | Відсутність верифікованого джерела |
| D2 | Маніпулювання контекстом | Зміщення часу/місця/обставин |
| D3 | Псевдоекспертиза | Відсутність атрибуції / непрозора методологія |
| D4 | Хибний причинно-наслідковий зв'язок / еквівокація | Явні логічні розриви |
| D5 | Шаблонне / синхронне повторення | Серії повторів, однотипні формулювання |
| D6 | Меметизація | Домінування мемної форми над фактичним повідомленням |
| D7 | Ознаки можливого організованого впливу | Лише при наявності відкритих індикаторів; не атрибуція |

**Правило:** D7 — лише за наявності додаткових відкритих індикаторів. Не використовувати для приписування координації або зовнішнього керування.

### 5.3. Коди наративів (N1–N10, `narrative_code`)

| Код | Суть |
|-----|------|
| N1 | Правове рамкування проти розширення прав |
| N2 | «Гендер» як ідеологічне питання |
| N3 | Сек'юритизація / антинаціональне рамкування |
| N4 | Виключення з публічного простору / заборона заходів |
| N5 | Апеляція до свободи слова як легітимація |
| N6 | Стамбульська конвенція як загроза порядку |
| N7 | «Захист дітей» / сексуальна небезпека |
| N8 | Спорт / «біологічна справедливість» |
| N9 | Медикалізоване / патологізувальне рамкування |
| N10 | Апеляція до інституційного авторитету |

**Правило:** один запис = один `narrative_code`. N-код описує сюжет / фрейм, а не дублює HS/D.

### 5.4. Операційна шкала ризику шкоди (`severity`)

| Рівень | Операційний опис | Публічна легенда |
|--------|-----------------|-----------------|
| 0 | HS/D-патернів не зафіксовано | Нейтральне згадування |
| 1 | Стигматизаційні узагальнення без закликів і без персональних даних | Стереотипізація |
| 2 | Підвищений ризик ворожості або виключення без прямих закликів | Дискримінаційний наратив |
| 3 | Вимоги обмеження прав, виключення, цільове переслідування або розкриття даних | Мова ворожнечі |
| 4 | Найвищий ризик фізичної шкоди, самосуду, підбурювання | Заклик до насильства |

### 5.5. `harm_flags` (мінімальний словник)

- `none`
- `discrimination_risk`
- `harassment_risk`
- `violence_risk`
- `doxxing_risk`

Додавання нових маркерів: обов'язкова дата затвердження та версія словника.

---

## 6. Методологія — response stream

### 6.1. Таксономія `response_action` (RA1–RA6)

| Код | Категорія | Доказова база |
|-----|-----------|---------------|
| RA1 | Модераційні заходи платформи (видалення, маркування, обмеження) | platform_notice / офіційна сторінка |
| RA2 | Адміністративно-регуляторна дія (приписи, рішення регулятора) | Офіційний вебсайт / PDF |
| RA3 | Офіційне листування або позиція (листи, роз'яснення, заяви) | Офіційний лист / вебсайт |
| RA4 | Звернення до правоохоронних органів / процесуальна дія | Офіційний реєстр / документ |
| RA5 | Судовий розгляд або рішення (позови, ухвали, рішення) | Реєстр судових рішень |
| RA6 | Некоерцитивна комунікація / профілактика (стандарти, контрнаративи, просвіта) | Офіційний вебсайт / OSINT |

**Правило:** `response_action` заповнювати ЛИШЕ коли `response_status` = `response_observed` або `in_progress`.

### 6.2. Словник `response_status`

| Статус | Опис | Умова застосування |
|--------|------|--------------------|
| `no_data` | Типовий (DEFAULT) — перевірки не здійснювали | Початковий стан |
| `no_response_found` | Перевірено — ознак реагування не виявлено | Після мінімального протоколу перевірки |
| `response_observed` | Реакцію верифіковано — є суб'єкт і джерело | Підтверджена дія |
| `in_progress` | Процес триває — відкрито провадження / надіслано запит | Без кінцевого результату |
| `not_applicable` | Механізм реагування об'єктивно неможливо застосувати | Виняток |

### 6.3. `verification_level`

- `mixed` (DEFAULT) — змішаний стандарт: офіційні сайти, реєстри, PDF, листи; OSINT лише як допоміжне
- `strict` — для окремих підвибірок з підвищеними вимогами

### 6.4. `response_evidence_type`

`official_website` | `official_registry` | `official_pdf` | `official_letter` | `platform_notice` | `osint_archived`

### 6.5. `responder` — нормалізація

Поля в `response_actions`: `responder_name_ua` (офіційна назва укр.), `responder_type`, `responder_level`.

**`responder_type`:** platform / regulator / ministry / parliament / local_council / ombudsperson / law_enforcement / court / ngo / other

**`responder_level`:** local / national / international / platform

Мінімальний довідник назв:
- Національна рада з питань ТРМ — regulator, national
- Міністерство юстиції України — ministry, national
- Секретаріат Уповноваженого ВРУ з прав людини — ombudsperson, national
- Комітет ВРУ (назва) — parliament, national
- Орган місцевого самоврядування — local_council, local
- Платформа / сервіс — platform, international

---

## 7. Архівування та перевірка посилань

- Пріоритет: `wayback` → `archive_today` → `local_pdf` (лише як виняток)
- `archived_at` і `archive_service` заповнюються одночасно з `archived_url`
- `last_checked_at` — щомісяця
- При недоступності URL — `archived_url` стає основним джерелом відтворюваності

---

## 8. Мінімізація повторної шкоди

### У публічному контурі ЗАБОРОНЕНО:
- Повні цитати мови ворожнечі
- Імена, контакти, адреси приватних осіб
- Аутинг, доксинг
- Скриншоти з персональними даними
- Дублювання чутливих архівних копій

### Дозволено публічно:
- `excerpt_neutral` (нейтральний переказ 1–2 речення без цитат і персональних даних)
- Агреговані дані
- Методологічні пояснення
- Словники кодів

---

## 9. Метрики якості даних (DQ1–DQ6)

Операційні показники повноти та відтворюваності корпусу. N = загальна кількість записів.

| Код | Показник | Формула |
|-----|----------|---------|
| DQ1 | Охоплення архівними адресами | count(archived_url NOT NULL) / N |
| DQ2 | Повнота даних про суб'єктів реагування | count(responder_name_ua NOT NULL WHERE status ∈ {response_observed, in_progress}) / count(status ∈ ...) |
| DQ3 | Повнота даних про типи дій | count(response_action NOT NULL WHERE status ∈ {observed, in_progress}) / count(status ∈ ...) |
| DQ4 | Повнота дат реагування | count(first_response_date NOT NULL WHERE status ∈ {observed, in_progress}) / count(status ∈ ...) |
| DQ5 | Частка no_data | count(response_status = 'no_data') / N |
| DQ6 | Актуальність перевірки посилань | Дата останнього `last_checked_at` — єдина для звітного циклу |

Оновлення DQ1–DQ5: щомісяця разом з оновленням корпусу.

---

## 10. Панель джерел моніторингу (A–F)

| Категорія | Мета | Типи джерел |
|-----------|------|-------------|
| A. Одноджерельний трекер | Внутрішня логіка та динаміка фреймів | Сайт одного видавця, архіви публікацій |
| B. Онлайн-медіа України | Потрапляння в мейнстрим | Загальнонаціональні та регіональні видання |
| C. Соціальні мережі | Хвилі, меметизація, синхронні повтори | Telegram, YouTube, Facebook, X, TikTok (лише публічний контент) |
| D. Інституційні джерела | Верифікація response stream | Офіційні сайти, реєстри рішень, судові реєстри |
| E. Громадянське суспільство | Некоерцитивні реакції, саморегуляція | Заяви ОГС, звіти медіарад |
| F. Міжнародний контекст | Транснаціональні прецеденти | Документи ЄС, міжнародних організацій |

**Збір:** B–C щотижня; D щомісяця; всі категорії щоквартально.

---

## 11. Версіонування і журнал змін

Обов'язкові версії: `schema_version`, `codebook_version`, `response_taxonomy_version`, `panel_version`.

Журнал змін (окремий файл/аркуш): дата, об'єкт, опис, причина, вплив на порівнянність, відповідальна роль.

---

## 12. Що це означає для адмін-панелі

### 12.1. `admin/record.html` — Phase 2

Після того як визначені конфлікти вирішені — потрібно додати UI для `response_actions`:
- Список реагувань по запису (може бути кілька)
- Поля: `response_status`, `responder_name_ua`, `responder_type`, `responder_level`
- Умовний `response_action` (disabled/hidden якщо status ∉ {response_observed, in_progress})
- `first_response_date`, `last_response_date`
- `lag_days` — read-only (GENERATED, не редагується)
- `response_url`, `response_archived_url`, `response_evidence_type`, `verification_level`

### 12.2. CSV export / `org_viewer`

НЕ виводити: `internal_notes`, `response_url`, `response_archived_url`, чутливі службові дані.

---

## 13. Публічна сторінка методології

### 13.1. Slug та SEO

```
/analityka/metodologiya-monitoryngu-movy-vorozhnechi-lgbtik-ukrayina/
```

**Meta title:** Методологія моніторингу мови ворожнечі щодо ЛГБТІК+ в Україні

**Meta description:** Пояснення методології моніторингу мови ворожнечі щодо ЛГБТІК+ в Україні: codebook, рівні ризику, наративи, верифікація реагування та принципи безпечної публікації даних.

### 13.2. Структура (8 блоків + FAQ)

1. **Hero** — eyebrow + H1 + lead + 2 CTA (інституційний доступ / долучитись до моніторингу)
2. **Навіщо ця методологія** — що вимірюється, чому відтворюваний стандарт, нейтральна мова, не правова кваліфікація
3. **Як працює система** — 4 кроки: intake → triage → verification → publication/aggregation
4. **Що кодується** — HS/D/N/severity/harm_flags/confidence + публічна легенда
5. **Як фіксується реагування** — response_status/responder/response_action/lag_days/verification
6. **Архівування** — wayback → archive.today → local PDF, `last_checked_at`
7. **Правила безпечної публікації** — без персональних даних, без цитат, без аутингу
8. **Межі інтерпретації** — панель ≠ репрезентативність; корпус ≠ prevalence; codebook ≠ legal qualification
- **FAQ** — 6 питань: HS-коди, D-коди, severity, чи публікуються URL, юридична кваліфікація, як долучитись
- **CTA** → /partnery/, /kontakty/, /monitor/, /prozorist/

### 13.3. JSON-LD

`Article` + `FAQPage` + `BreadcrumbList`

### 13.4. Що НЕ показувати публічно

- Сирі URL кейсів
- `internal_notes`
- Чутливі архівні копії
- `response_url` / `response_archived_url`
- Персональні дані

---

## 14. Що це означає для `analityka/post/index.html`

Шаблон має підтримати:
- Довгі технічні секції (H2, H3, таблиці)
- FAQ-блок (`<details>/<summary>`)
- Sticky sidebar / anchor TOC
- CTA-блок в кінці
- Блок "межі інтерпретації"
- Виноска про безпечну публікацію
- JSON-LD з `Article` + `FAQPage`

---

## 15. Acceptance criteria

### DB
- `response_actions` table exists та містить всі response поля ✅ (вже є)
- `lag_days >= 0` CHECK додано ⏳
- `internal_notes` не потрапляє в публічні views ✅ (контролюється в коді)

### Публічна сторінка
- Окремий slug ✅
- SEO head, H1, структура H2
- Пояснення HS/D/N/severity
- Response stream блок
- Мінімізація повторної шкоди
- Межі інтерпретації
- CTA
- Сторінка не публікує чутливі дані

---

## 16. Що Claude Code НЕ повинен робити

- Не додавати response поля inline до `records` — вони вже в `response_actions`
- Не створювати тригер для `lag_days` — він GENERATED ALWAYS
- Не нумерувати нові response міграції як 009 — вона зайнята
- Не спрощувати методологію до PR-тексту
- Не замінювати терміни методології довільними синонімами
- Не будувати методологічну сторінку як суто маркетингову
- Не робити публічний каталог кейсів з сирими URL
- Не чіпати `config.js`, env vars, секрети
- Не виносити `internal_notes` публічно
