From 71fd48aa06712879d637d78d02ad0c0d93316610 Mon Sep 17 00:00:00 2001 From: kpa39l Date: Sun, 13 Sep 2026 17:48:06 +0000 Subject: [PATCH] Session 2026-09-13: unify directions (canon+LLM-fallback, 966/1019), publisher dry_run fix, git repo, archive 8 changes, candidates-mail-ui WIP, docs --- STATUS.md | 19 ++- TODO.md | 7 +- WALKTHROUGH.md | 38 ++++++ .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../2026-09-13-fix-approve-dirn}/design.md | 0 .../2026-09-13-fix-approve-dirn}/proposal.md | 0 .../2026-09-13-fix-approve-dirn}/tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../2026-09-13-unify-directions}/design.md | 0 .../2026-09-13-unify-directions}/proposal.md | 0 .../2026-09-13-unify-directions}/tasks.md | 8 +- .../.openspec.yaml | 0 .../2026-09-13-web-format-datetime}/design.md | 0 .../proposal.md | 0 .../2026-09-13-web-format-datetime}/tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../2026-09-13-web-media-preview}/design.md | 0 .../2026-09-13-web-media-preview}/proposal.md | 0 .../2026-09-13-web-media-preview}/tasks.md | 0 .../.openspec.yaml | 0 .../2026-09-13-web-render-markdown}/design.md | 0 .../proposal.md | 0 .../2026-09-13-web-render-markdown}/tasks.md | 0 .../changes/candidates-mail-ui/.openspec.yaml | 2 + openspec/changes/candidates-mail-ui/design.md | 65 +++++++++++ .../changes/candidates-mail-ui/proposal.md | 45 ++++++++ .../specs/candidates-mail-ui/spec.md | 108 ++++++++++++++++++ openspec/changes/candidates-mail-ui/tasks.md | 20 ++++ 40 files changed, 305 insertions(+), 7 deletions(-) rename openspec/changes/{deexternalize-web-assets => archive/2026-09-13-deexternalize-web-assets}/.openspec.yaml (100%) rename openspec/changes/{deexternalize-web-assets => archive/2026-09-13-deexternalize-web-assets}/design.md (100%) rename openspec/changes/{deexternalize-web-assets => archive/2026-09-13-deexternalize-web-assets}/proposal.md (100%) rename openspec/changes/{deexternalize-web-assets => archive/2026-09-13-deexternalize-web-assets}/tasks.md (100%) rename openspec/changes/{fix-approve-dirn => archive/2026-09-13-fix-approve-dirn}/.openspec.yaml (100%) rename openspec/changes/{fix-approve-dirn => archive/2026-09-13-fix-approve-dirn}/design.md (100%) rename openspec/changes/{fix-approve-dirn => archive/2026-09-13-fix-approve-dirn}/proposal.md (100%) rename openspec/changes/{fix-approve-dirn => archive/2026-09-13-fix-approve-dirn}/tasks.md (100%) rename openspec/changes/{publisher-dry-run-fix => archive/2026-09-13-publisher-dry-run-fix}/.openspec.yaml (100%) rename openspec/changes/{publisher-dry-run-fix => archive/2026-09-13-publisher-dry-run-fix}/design.md (100%) rename openspec/changes/{publisher-dry-run-fix => archive/2026-09-13-publisher-dry-run-fix}/proposal.md (100%) rename openspec/changes/{publisher-dry-run-fix => archive/2026-09-13-publisher-dry-run-fix}/tasks.md (100%) rename openspec/changes/{unify-directions => archive/2026-09-13-unify-directions}/.openspec.yaml (100%) rename openspec/changes/{unify-directions => archive/2026-09-13-unify-directions}/design.md (100%) rename openspec/changes/{unify-directions => archive/2026-09-13-unify-directions}/proposal.md (100%) rename openspec/changes/{unify-directions => archive/2026-09-13-unify-directions}/tasks.md (54%) rename openspec/changes/{web-format-datetime => archive/2026-09-13-web-format-datetime}/.openspec.yaml (100%) rename openspec/changes/{web-format-datetime => archive/2026-09-13-web-format-datetime}/design.md (100%) rename openspec/changes/{web-format-datetime => archive/2026-09-13-web-format-datetime}/proposal.md (100%) rename openspec/changes/{web-format-datetime => archive/2026-09-13-web-format-datetime}/tasks.md (100%) rename openspec/changes/{web-list-filter-no-js => archive/2026-09-13-web-list-filter-no-js}/.openspec.yaml (100%) rename openspec/changes/{web-list-filter-no-js => archive/2026-09-13-web-list-filter-no-js}/design.md (100%) rename openspec/changes/{web-list-filter-no-js => archive/2026-09-13-web-list-filter-no-js}/proposal.md (100%) rename openspec/changes/{web-list-filter-no-js => archive/2026-09-13-web-list-filter-no-js}/tasks.md (100%) rename openspec/changes/{web-media-preview => archive/2026-09-13-web-media-preview}/.openspec.yaml (100%) rename openspec/changes/{web-media-preview => archive/2026-09-13-web-media-preview}/design.md (100%) rename openspec/changes/{web-media-preview => archive/2026-09-13-web-media-preview}/proposal.md (100%) rename openspec/changes/{web-media-preview => archive/2026-09-13-web-media-preview}/tasks.md (100%) rename openspec/changes/{web-render-markdown => archive/2026-09-13-web-render-markdown}/.openspec.yaml (100%) rename openspec/changes/{web-render-markdown => archive/2026-09-13-web-render-markdown}/design.md (100%) rename openspec/changes/{web-render-markdown => archive/2026-09-13-web-render-markdown}/proposal.md (100%) rename openspec/changes/{web-render-markdown => archive/2026-09-13-web-render-markdown}/tasks.md (100%) create mode 100644 openspec/changes/candidates-mail-ui/.openspec.yaml create mode 100644 openspec/changes/candidates-mail-ui/design.md create mode 100644 openspec/changes/candidates-mail-ui/proposal.md create mode 100644 openspec/changes/candidates-mail-ui/specs/candidates-mail-ui/spec.md create mode 100644 openspec/changes/candidates-mail-ui/tasks.md diff --git a/STATUS.md b/STATUS.md index 436271b..0959d9f 100644 --- a/STATUS.md +++ b/STATUS.md @@ -97,6 +97,21 @@ curl -s -X POST http://127.0.0.1:8410/api/v1/publish \ CLASSIFY_TIMEOUT=20 .venv/bin/python -m classifier.classify --db db/vesti.db --limit 200 ``` +## Сделано (сессия закрытия 2026-09-13) +- [x] **Классификация направлений приведена к канону** `linux, tech, politics, games, electronics, llm` (AGENT.MD): keywords.py (add politics/llm, dev→tech, убрать media/ai, +golang), classify.py (промпт из DIRECTIONS_CANON + **LLM-fallback при dict-miss**: словарь не дал → всё равно LLM), web/app.py (фильтр из канона). Change `unify-directions` архивирован. +- [x] **Переклассификация**: 503 (466 своих + 37 внешних) + 206 (сброшены dev/media/ai) → 966/1019 классифицированы; без direction осталось 53 (шум/личное: музыка, поздравления — не новости). Старые мусорные dev/media/ai устранены. +- [x] **git-репозиторий** /opt/vesti: origin gitverse.ru `kpa39l/vesti` (main, private), зеркало gitea bigbox `estorozhenko/vesti` (mirror 8h). .gitignore: backups/ media/ db/ bundles/ logs/ секреты. +- [x] **Тест публикации с медиа сквозь веб**: сообщения ушли в @dedinit_vesti (11/12) и удалены. Обнаружен и исправлен фейковый `dry_run` в publisher (поле уехало в PublishResponse вместо PublishRequest; собыл 500). Change `publisher-dry-run-fix` архивирован: `dry_run=true` → эмуляция без TG. +- [x] **OpenSpec changes архивированы** (8): fix-approve-dirn, web-media-preview, web-format-datetime, web-list-filter-no-js, deexternalize-web-assets, web-render-markdown, publisher-dry-run-fix, unify-directions. +- [x] **Новый change `candidates-mail-ui`** (двухпанельный интерфейс кандидатов, готовится; proposal/design/tasks/spec — валиден). + +## Текущие задачи (из TODO, открытые) +- Интерфейс кандидатов как почтовый клиент (2 панели, группировка, bulk) — change candidates-mail-ui (проработать с пользователем) +- Ручное указание/изменение направления (значок «?» у неклассифицированных) +- Переписывание новостей от себя (LLM-пересказ, персональный контент) +- Извлечение источников из чужих постов + сервис поиска источников в интернете (дообогащение ТОЛЬКО одобренных) +- Страница управления источниками (CRUD + фильтр по типу: Telegram, RSS, почта, сайты, Twitter) + ## Ключевые артефакты - /opt/vesti/AGENT.MD — правила проекта (обязательно: все изменения через OpenSpec) - /opt/vesti/openspec/changes/{deexternalize-web-assets, web-render-markdown, web-format-datetime, web-list-filter-no-js}/ — 4 change веб-интерфейса (валидны, задачи [x]) @@ -113,6 +128,6 @@ CLASSIFY_TIMEOUT=20 .venv/bin/python -m classifier.classify --db db/vesti.db --l - **publisher в проде = Docker** (2026-09-13): systemd1 (D-Bus org.freedesktop.systemd1) после перезагрузки ожил, контейнер vesti-publisher (compose, restart=unless-stopped) работает healthy. User-юнит vesti-publisher.service выключен (disabled) и не используется. Если снова сломается systemd1 — вернуть юнит: systemctl --user enable --now vesti-publisher.service. - Fan-out: сейчас все каналы из VESTI_BOT_CHANNELS (один). В будущем — направления→каналы (маппинг). - ~~Бэкфилл канала (задача 2.3)~~ — ЗАВЕРШЁН (2026-09-10): 1040 постов, 846 в БД is_own=1 -- git-репозиторий /opt/vesti — инициализировать? +- ~~git-репозиторий /opt/vesti~~ — СДЕЛАН (2026-09-13): gitverse kpa39l/vesti (main) + gitea-зеркало bigbox (8h) - systemd daemon-reload на bigbox зависает (внешняя проблема, не vesti) — юнит подхватится при перезагрузке -- Качество классификации своих постов (846, словари vs LLM) — проверить выборочно \ No newline at end of file +- ~~Качество классификации~~ — ПРИВЕДЕНО К КАНОНУ (2026-09-13): 966/1019 с direction; 53 без — шум/личное \ No newline at end of file diff --git a/TODO.md b/TODO.md index 5d178a5..cedc222 100644 --- a/TODO.md +++ b/TODO.md @@ -53,4 +53,9 @@ | 2026-09-13 | Бэкап VESTI на Яндекс.Диск (по аналогии icq/netbox) | ✅ закрыта | 2026-09-13 (backup.sh + backups/ + ЯД vesti-backups, cron root 45 2) | | 2026-09-13 | approve → 500 (UnboundLocalError 'dirn' в web/app.py) | ✅ закрыта | 2026-09-13 (change fix-approve-dirn; dirn/lang перенесены до использования; 303 вместо 500) | | 2026-09-13 | Медиа в карточках новостей не отображаются | ✅ закрыта | 2026-09-13 (change web-media-preview; роут /media/{filename} + превью img/video в candidates/published; клик → полноразмер в новой вкладке) | -| 2026-09-13 | Пост без классификации: на карточке значок «?» — понять его роль; если классификации нет — дать возможность вручную указывать/изменять/добавлять направление при анализе | 🔵 открыта | | \ No newline at end of file +| 2026-09-13 | Пост без классификации: на карточке значок «?» — понять его роль; если классификации нет — дать возможность вручную указывать/изменять/добавлять направление при анализе | 🔵 открыта | | +| 2026-09-13 | git-репозиторий /opt/vesti (gitverse истина, gitea зеркало) | ✅ закрыта | 2026-09-13 (git init + commit; gitverse kpa39l/vesti main; gitea estorozhenko/vesti mirror 8h; .gitignore: backups/media/db/bundles/logs исключены) | +| 2026-09-13 | Интерфейс кандидатов: разделить окно на 2 панели — слева список кандидатов, справа меню действий с постом (опубликовать / отклонить / переклассифицировать / переписать новость под себя), ниже блок для моего комментария, потом оригинальный пост со всеми медиа | 🔵 открыта | (зафиксирована, проработать подробности) | +| 2026-09-13 | Контент-стратегия: не перепубликовать чужие новости, а публиковать переписанные от себя (персональный контент, добавление ценности). Связано с «переписать новость под себя» в интерфейсе кандидатов — нужна генерация пересказа через LLM + редактирование перед публикацией | 🔵 открыта | (идея, проработать) | +| 2026-09-13 | Извлечение источников новостей из полученных постов (каналы/сайты, откуда пришли новости) — чтобы в следующий раз забирать новости напрямую из источника. ДОПОЛНЕНИЕ: возможно стоит разработать сервис поиска источников в интернете (если источник не указан) и ДООБОГАЩАТЬ карточки кандидатов, но — чтобы не тратить лишние деньги — ТОЛЬКО для одобренных кандидатов. Пайплайн: отбор новостей из списка кандидатов → в «выбранные» → обработка/дообогащение/переписывание при необходимости → публикация | 🔵 открыта | (зафиксирована, проработать) | +| 2026-09-13 | Отдельная страница управления источниками новостей: отображение / добавление / удаление / редактирование, фильтр по типу источника (Telegram, RSS, почтовая рассылка, прямые новости с сайтов, Twitter и т.п.) | 🔵 открыта | (зафиксирована, проработать) | \ No newline at end of file diff --git a/WALKTHROUGH.md b/WALKTHROUGH.md index 24c37c2..fb1015b 100644 --- a/WALKTHROUGH.md +++ b/WALKTHROUGH.md @@ -401,3 +401,41 @@ sudo systemctl restart vesti-web # active; login → 200, candidates/published 2. Тест публикаци с медиа сквозь веб (approve с media_path → publisher send_photo) — если ещё не проверен. 3. git-репозиторий /opt/vesti (gitverse истина, gitea зеркало). 4. openspec archive: fix-approve-dirn, web-media-preview, rich-repost-card + старые changes (после подтверждения). + +--- + +## Сессия 2026-09-13 (закрытие проекта vezde + новые фичи) + +### 1. Унификация направлений классификации (change unify-directions, архивирован) +Проблема: список направлений разъехался по 4 местам (classify.py:87 промпт, web/app.py:130 фильтр, keywords.py словари, канон AGENT.MD). +- Канон: **linux, tech, politics, games, electronics, llm**. +- keywords.py: `DIRECTIONS_CANON` + словари приведены (dev→tech, ai→llm, media удалён, politics добавлен). +- classify.py: промпт LLM из `DIRECTIONS_CANON`; импорт `find_direction, DIRECTIONS_CANON`. +- **LLM-fallback**: раньше LLM звалась ТОЛЬКО если словарь дал direction; при dict-miss пост оставался серым навсегда. Теперь при dict-miss тоже зовём LLM (проверка канона). Это убрало 449 серых постов. +- Убраны однобуквенные ключи `"c"`/`"go"` (шумят: `\bc\b` матчится на любую `c`). `qt/vr/ии/ai/ml` — валидны (границы слов). +- web/app.py: фильтр направлений = канон. +- **Питфол**: keywords.py перезаписан write_file → обрезался (117→99 строк, потерян хвост find_directions_all/find_direction/__main__). Восстанавливать по контракту из classify.py (find_direction→str|None, find_directions_all→list[str]). +- Переклассификация: 503 (466 своих+37 внешних) + 206 сброшенных (dev/media/ai→classified=0) → **966/1019** классифицированы. Осталось 53 без direction — шум/личное (музыка, поздравления), не новости. + +### 2. Тест публикации с медиа + фикс dry_run (change publisher-dry-run-fix, архивирован) +- Тест: `POST /api/v1/publish` с dry_run:true + media → сообщения РЕАЛЬНО ушли в @dedinit_vesti (message_id 11=медиа, 12=текст). **dry_run был мёртвой опцией** (поле в PublishResponse, а не PublishRequest). +- Исправлено: `dry_run: bool = False` в PublishRequest; в роуте — ветка «эмулировать результат» (message_id=0, dry_run=true) ДО цикла по каналам. +- **Питфол**: после первой пересборки — 500 (AttributeError: dry_run в PublishResponse). Поле добавлено в PublishRequest → работает: dry_run=true → {ok, message_id:0, dry_run:true}, в TG ничего не уходит. +- Тестовые сообщения удалены через `delete_message('@dedinit_vesti', 11/12)` (в publisher есть такой метод). + +### 3. git-репозиторий (задача «git репозиторий?») +- `git init -b main`, .gitignore (backups/ media/ db/ bundles/ logs/ секреты), первый коммит c3f59f7. +- gitverse: `POST $GITVERSE_API/user/repos` (Bearer PAT + Accept `application/vnd.gitverse.object+json;version=1`, auto_init:false) → kpa39l/vesti (id 334405). Remote: `https://kpa39l:@gitverse.ru/kpa39l/vesti.git`. +- Gitea-зеркало bigbox: `POST :3000/api/v1/repos/migrate` (clone_addr `https://oauth2:@gitverse.ru/...`, mirror:true, mirror_interval:8h) → estorozhenko/vesti. +- **Питфол**: первый migrate таймаутнул (180с — gitea клонировал) и оставил пустой repo с mirror=True но без данных → «Repository is not a mirror» на mirror-sync. Удалить (DELETE) и пересоздать через migrate заново (дождаться 201 — миграция синхронизирует при создании). + +### 4. Классификация: задачи и термины +- Новые задачи в TODO (5 открытых): интерфейс кандидатов (почтовый клиент), ручное направление («?» = direction пустой — см. candidates.html:41), переписывание от себя (LLM-пересказ), извлечение/поиск источников (дообогащение только одобренных), страница управления источниками (CRUD+фильтр типов). +- Change **candidates-mail-ui** (готовится): 2 панели (список слева + детально справа), группировка по source/date/status, bulk approve/reject, comment, reclassify, rewrite, медиа. proposal/design/tasks/spec — валидны. + +### Следующая сессия +1. Проработать детали **candidates-mail-ui** с пользователем (открытые вопросы: комментарий — поле/таблица?, переписывание — qwen черновик?, переклассификация — перезапуск?, группировка — одна/несколько) → реализовать. +2. Ручное указание направления (для 53 без direction + «?» на карточке). +3. Переписывание новостей от себя (LLM-пересказ) + пайплайн «выбранные → дообогащение → публикация». +4. Извлечение источников из постов + сервис поиска в интернете (только одобренные). +5. Страница управления источниками (CRUD + фильтр по типу). diff --git a/openspec/changes/deexternalize-web-assets/.openspec.yaml b/openspec/changes/archive/2026-09-13-deexternalize-web-assets/.openspec.yaml similarity index 100% rename from openspec/changes/deexternalize-web-assets/.openspec.yaml rename to openspec/changes/archive/2026-09-13-deexternalize-web-assets/.openspec.yaml diff --git a/openspec/changes/deexternalize-web-assets/design.md b/openspec/changes/archive/2026-09-13-deexternalize-web-assets/design.md similarity index 100% rename from openspec/changes/deexternalize-web-assets/design.md rename to openspec/changes/archive/2026-09-13-deexternalize-web-assets/design.md diff --git a/openspec/changes/deexternalize-web-assets/proposal.md b/openspec/changes/archive/2026-09-13-deexternalize-web-assets/proposal.md similarity index 100% rename from openspec/changes/deexternalize-web-assets/proposal.md rename to openspec/changes/archive/2026-09-13-deexternalize-web-assets/proposal.md diff --git a/openspec/changes/deexternalize-web-assets/tasks.md b/openspec/changes/archive/2026-09-13-deexternalize-web-assets/tasks.md similarity index 100% rename from openspec/changes/deexternalize-web-assets/tasks.md rename to openspec/changes/archive/2026-09-13-deexternalize-web-assets/tasks.md diff --git a/openspec/changes/fix-approve-dirn/.openspec.yaml b/openspec/changes/archive/2026-09-13-fix-approve-dirn/.openspec.yaml similarity index 100% rename from openspec/changes/fix-approve-dirn/.openspec.yaml rename to openspec/changes/archive/2026-09-13-fix-approve-dirn/.openspec.yaml diff --git a/openspec/changes/fix-approve-dirn/design.md b/openspec/changes/archive/2026-09-13-fix-approve-dirn/design.md similarity index 100% rename from openspec/changes/fix-approve-dirn/design.md rename to openspec/changes/archive/2026-09-13-fix-approve-dirn/design.md diff --git a/openspec/changes/fix-approve-dirn/proposal.md b/openspec/changes/archive/2026-09-13-fix-approve-dirn/proposal.md similarity index 100% rename from openspec/changes/fix-approve-dirn/proposal.md rename to openspec/changes/archive/2026-09-13-fix-approve-dirn/proposal.md diff --git a/openspec/changes/fix-approve-dirn/tasks.md b/openspec/changes/archive/2026-09-13-fix-approve-dirn/tasks.md similarity index 100% rename from openspec/changes/fix-approve-dirn/tasks.md rename to openspec/changes/archive/2026-09-13-fix-approve-dirn/tasks.md diff --git a/openspec/changes/publisher-dry-run-fix/.openspec.yaml b/openspec/changes/archive/2026-09-13-publisher-dry-run-fix/.openspec.yaml similarity index 100% rename from openspec/changes/publisher-dry-run-fix/.openspec.yaml rename to openspec/changes/archive/2026-09-13-publisher-dry-run-fix/.openspec.yaml diff --git a/openspec/changes/publisher-dry-run-fix/design.md b/openspec/changes/archive/2026-09-13-publisher-dry-run-fix/design.md similarity index 100% rename from openspec/changes/publisher-dry-run-fix/design.md rename to openspec/changes/archive/2026-09-13-publisher-dry-run-fix/design.md diff --git a/openspec/changes/publisher-dry-run-fix/proposal.md b/openspec/changes/archive/2026-09-13-publisher-dry-run-fix/proposal.md similarity index 100% rename from openspec/changes/publisher-dry-run-fix/proposal.md rename to openspec/changes/archive/2026-09-13-publisher-dry-run-fix/proposal.md diff --git a/openspec/changes/publisher-dry-run-fix/tasks.md b/openspec/changes/archive/2026-09-13-publisher-dry-run-fix/tasks.md similarity index 100% rename from openspec/changes/publisher-dry-run-fix/tasks.md rename to openspec/changes/archive/2026-09-13-publisher-dry-run-fix/tasks.md diff --git a/openspec/changes/unify-directions/.openspec.yaml b/openspec/changes/archive/2026-09-13-unify-directions/.openspec.yaml similarity index 100% rename from openspec/changes/unify-directions/.openspec.yaml rename to openspec/changes/archive/2026-09-13-unify-directions/.openspec.yaml diff --git a/openspec/changes/unify-directions/design.md b/openspec/changes/archive/2026-09-13-unify-directions/design.md similarity index 100% rename from openspec/changes/unify-directions/design.md rename to openspec/changes/archive/2026-09-13-unify-directions/design.md diff --git a/openspec/changes/unify-directions/proposal.md b/openspec/changes/archive/2026-09-13-unify-directions/proposal.md similarity index 100% rename from openspec/changes/unify-directions/proposal.md rename to openspec/changes/archive/2026-09-13-unify-directions/proposal.md diff --git a/openspec/changes/unify-directions/tasks.md b/openspec/changes/archive/2026-09-13-unify-directions/tasks.md similarity index 54% rename from openspec/changes/unify-directions/tasks.md rename to openspec/changes/archive/2026-09-13-unify-directions/tasks.md index 4a8b676..ea1bbd0 100644 --- a/openspec/changes/unify-directions/tasks.md +++ b/openspec/changes/archive/2026-09-13-unify-directions/tasks.md @@ -6,7 +6,7 @@ - [x] classify.py:87: промпт → DIRECTIONS_CANON - [x] classify.py: LLM-fallback при dict-miss (словарь не дал → LLM, проверка канона) — убирает серые посты - [x] web/app.py:130: directions → DIRECTIONS_CANON -- [ ] Переклассификация 466 своих без direction (limit 1000) -- [ ] Проверка БД: нет новых направлений вне канона -- [ ] `openspec validate unify-directions` — чисто -- [ ] Бэкап после правки \ No newline at end of file +- [x] Переклассификация 466 своих без direction (limit 1000) — 503+206 постов, LLM-fallback добор +- [x] Проверка БД: нет новых направлений вне канона (dev/media/ai устранены; осталось 53 без direction — шум/личное, не новости) +- [x] `openspec validate unify-directions` — чисто +- [x] Бэкап после правки — `/tmp/vesti.db.bak-unify` + будет полный бэкап \ No newline at end of file diff --git a/openspec/changes/web-format-datetime/.openspec.yaml b/openspec/changes/archive/2026-09-13-web-format-datetime/.openspec.yaml similarity index 100% rename from openspec/changes/web-format-datetime/.openspec.yaml rename to openspec/changes/archive/2026-09-13-web-format-datetime/.openspec.yaml diff --git a/openspec/changes/web-format-datetime/design.md b/openspec/changes/archive/2026-09-13-web-format-datetime/design.md similarity index 100% rename from openspec/changes/web-format-datetime/design.md rename to openspec/changes/archive/2026-09-13-web-format-datetime/design.md diff --git a/openspec/changes/web-format-datetime/proposal.md b/openspec/changes/archive/2026-09-13-web-format-datetime/proposal.md similarity index 100% rename from openspec/changes/web-format-datetime/proposal.md rename to openspec/changes/archive/2026-09-13-web-format-datetime/proposal.md diff --git a/openspec/changes/web-format-datetime/tasks.md b/openspec/changes/archive/2026-09-13-web-format-datetime/tasks.md similarity index 100% rename from openspec/changes/web-format-datetime/tasks.md rename to openspec/changes/archive/2026-09-13-web-format-datetime/tasks.md diff --git a/openspec/changes/web-list-filter-no-js/.openspec.yaml b/openspec/changes/archive/2026-09-13-web-list-filter-no-js/.openspec.yaml similarity index 100% rename from openspec/changes/web-list-filter-no-js/.openspec.yaml rename to openspec/changes/archive/2026-09-13-web-list-filter-no-js/.openspec.yaml diff --git a/openspec/changes/web-list-filter-no-js/design.md b/openspec/changes/archive/2026-09-13-web-list-filter-no-js/design.md similarity index 100% rename from openspec/changes/web-list-filter-no-js/design.md rename to openspec/changes/archive/2026-09-13-web-list-filter-no-js/design.md diff --git a/openspec/changes/web-list-filter-no-js/proposal.md b/openspec/changes/archive/2026-09-13-web-list-filter-no-js/proposal.md similarity index 100% rename from openspec/changes/web-list-filter-no-js/proposal.md rename to openspec/changes/archive/2026-09-13-web-list-filter-no-js/proposal.md diff --git a/openspec/changes/web-list-filter-no-js/tasks.md b/openspec/changes/archive/2026-09-13-web-list-filter-no-js/tasks.md similarity index 100% rename from openspec/changes/web-list-filter-no-js/tasks.md rename to openspec/changes/archive/2026-09-13-web-list-filter-no-js/tasks.md diff --git a/openspec/changes/web-media-preview/.openspec.yaml b/openspec/changes/archive/2026-09-13-web-media-preview/.openspec.yaml similarity index 100% rename from openspec/changes/web-media-preview/.openspec.yaml rename to openspec/changes/archive/2026-09-13-web-media-preview/.openspec.yaml diff --git a/openspec/changes/web-media-preview/design.md b/openspec/changes/archive/2026-09-13-web-media-preview/design.md similarity index 100% rename from openspec/changes/web-media-preview/design.md rename to openspec/changes/archive/2026-09-13-web-media-preview/design.md diff --git a/openspec/changes/web-media-preview/proposal.md b/openspec/changes/archive/2026-09-13-web-media-preview/proposal.md similarity index 100% rename from openspec/changes/web-media-preview/proposal.md rename to openspec/changes/archive/2026-09-13-web-media-preview/proposal.md diff --git a/openspec/changes/web-media-preview/tasks.md b/openspec/changes/archive/2026-09-13-web-media-preview/tasks.md similarity index 100% rename from openspec/changes/web-media-preview/tasks.md rename to openspec/changes/archive/2026-09-13-web-media-preview/tasks.md diff --git a/openspec/changes/web-render-markdown/.openspec.yaml b/openspec/changes/archive/2026-09-13-web-render-markdown/.openspec.yaml similarity index 100% rename from openspec/changes/web-render-markdown/.openspec.yaml rename to openspec/changes/archive/2026-09-13-web-render-markdown/.openspec.yaml diff --git a/openspec/changes/web-render-markdown/design.md b/openspec/changes/archive/2026-09-13-web-render-markdown/design.md similarity index 100% rename from openspec/changes/web-render-markdown/design.md rename to openspec/changes/archive/2026-09-13-web-render-markdown/design.md diff --git a/openspec/changes/web-render-markdown/proposal.md b/openspec/changes/archive/2026-09-13-web-render-markdown/proposal.md similarity index 100% rename from openspec/changes/web-render-markdown/proposal.md rename to openspec/changes/archive/2026-09-13-web-render-markdown/proposal.md diff --git a/openspec/changes/web-render-markdown/tasks.md b/openspec/changes/archive/2026-09-13-web-render-markdown/tasks.md similarity index 100% rename from openspec/changes/web-render-markdown/tasks.md rename to openspec/changes/archive/2026-09-13-web-render-markdown/tasks.md diff --git a/openspec/changes/candidates-mail-ui/.openspec.yaml b/openspec/changes/candidates-mail-ui/.openspec.yaml new file mode 100644 index 0000000..f9b58f5 --- /dev/null +++ b/openspec/changes/candidates-mail-ui/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-13 \ No newline at end of file diff --git a/openspec/changes/candidates-mail-ui/design.md b/openspec/changes/candidates-mail-ui/design.md new file mode 100644 index 0000000..f496a72 --- /dev/null +++ b/openspec/changes/candidates-mail-ui/design.md @@ -0,0 +1,65 @@ +# design.md — candidates-mail-ui + +## Текущая структура (из чтения кода) + +- `web/app.py` (FastAPI) — роуты: + - `GET /candidates` — рендер `candidates.html` (список карточек). + - `POST /posts/{id}/approve` — апрув (публикация через publisher). + - `POST /posts/{id}/reject` (или аналогичный) — отклонение. + - GET /media/{filename} — медиа (добавлен в web-media-preview). +- Таблица `posts`: id, source_id (FK), text, direction, lang, views, reactions, + media_path, status (new/published/rejected?), classified, created_at. +- Таблица `classifications`: post_id, direction, relevance, interest, summary, model. + +## Архитектура нового UI + +### 1. Левая панель: список + группировка + bulk + +- Один маршрут `GET /candidates` с query-параметрами: + - `group_by` ∈ {source, date, status} (по умолчанию source) + - `filter` (статус: new/published/rejected/all) + - `q` (поиск по тексту) +- Ответ: тот же HTML (серверный рендер, Jinja2), но layout — две колонки + (Bootstrap 5 grid, уже подключён). +- Список строится из постов (new/rejected), сгруппированных по выбранному + признаку. Группы — сворачиваемые (`
`/`` — без JS). +- Каждый пост имеет чекбокс ``. +- Форма bulk: `POST /candidates/bulk` с `ids[]` и `action` (approve/reject/reject-old). + +### 2. Правая панель: детальный просмотр + действия + +- Выбранный пост: `GET /candidates/{id}` (или та же страница с `?selected=`). +- Секции: + - Кнопки действий: Опубликовать, Отклонить, Переклассифицировать, + Переписать новость (LLM). + - «Мой комментарий»: `