# re-publisher

Очистка текста, извлечение HTML, классификация и подготовка черновика с видимыми этапами обработки. В публичном контуре отключены внешний сбор и публикация.

Статус: passed

Версия: `b13048fbc5490d161d7a89ad9c72b4b74bafc548`. История кода: с 16 июня 2026. Подготовка демо: 7 сентября 2026.

## Задача

Редакционный конвейер должен отделять исходник, промежуточные признаки и черновик, чтобы результат можно было проверить и повторить после ошибки.

## Аудитория

Редакторы тематических каналов и разработчики систем обработки контента.

## Личный вклад

- Модули нормализации, извлечения, классификации и шаблонного редактирования.

- История обработки с результатами каждого этапа.

- Изолированный публичный runtime и исправление опасного workflow деплоя.

## Решения

- Публичная точка входа не монтирует collector и операционный admin.

- Используются исходные processed_values, extract_html, predict и draft_from_template.

- TF-IDF и LogisticRegression выполняются реально на фиксированном синтетическом наборе.

- Повтор управляемого сбоя обновляет тот же run ID, сохраняя принадлежность сессии.

## Границы и ограничения

- Telegram input заменён тремя собственными материалами; LLM заменена шаблонным редактором.

- Шесть обучающих примеров не позволяют заявлять качество классификатора на независимых данных.

- Внешняя публикация и произвольные URL/загрузки отключены. Два одновременных выполнения, 20 материалов на сессию, TTL один час.

## Что попробовать

- Выберите один из трёх материалов и запустите обработку.

- Просмотрите очищенный текст, извлечение, классификацию и черновик.

- Включите пример ошибки извлечения, затем повторите тот же материал.

- Сравните свою историю со второй сессией и сбросьте свой пример.

## Проверки

- 46 unit-тестов pipeline и исходных content-модулей пройдено.

- Other session cannot read/retry/reset own material; signed expiring cookie

- Failure then retry uses same ID; retry ready draft returns 409

- Only fixed fixture IDs; arbitrary URL and extra fields rejected

- Подтверждённого GitHub CI для этого SHA нет; перечислены выполненные локальные проверки и сценарии опубликованной версии. Исторические CI-ссылки, если есть, отмечены отдельно.

## Результаты

- Сохранено 13 содержательных кадров фактического интерфейса.

- Дата, сценарий и версия указаны у каждого кадра.

- Максимум RAM контейнеров в коротком тесте: 213.2 MiB. Общие службы учитываются отдельно; магазин включает свою БД.

- Измерено 6 повторений сценария; медиана 1730.0 мс, максимум 2992 мс. Время включает браузер и HTTPS; методика и ограничения приложены.

## Запуск и эксплуатация

- Исходный контур содержит Telegram collector и рабочую схему хранения. Демо использует отдельную таблицу portfolio_runs и отдельный entrypoint; эти архитектуры не отождествляются.

- Сборка: docker build -f Dockerfile.portfolio -t portfolio-publisher:VERSION . Полный requirements.portfolio.lock закрепляет runtime. DB_DSN и PORTFOLIO_SECRET передаются на сервере и не входят в образ.

- API доступен через localhost:18404. Healthcheck /healthz проверяет БД. При ошибке изучить stage result и повторить тот же run ID; записи другой сессии недоступны. Cleanup каждые пять минут. Исходный workflow заменён ручной проверкой без SSH/prune/удаления логов.

- Общие правила эксплуатации: готовые файлы и закреплённые образы собираются вне VPS; секреты не входят в артефакты. Общесерверный docker prune не применяется. Логи backend ограничены ротацией, изменения log options требуют пересоздания контейнера.

- Перед изменением пройти code-map от функции к экрану/API, модулю и данным. После изменения обновить карту и схемы, выполнить связанные сценарии, включая ошибку, повтор и сброс. Источник истины — конкретная версия кода, а не старая диаграмма.

- Откат проверен 2026-09-07T01:05:09.948422+00:00: 20260907-verified → 20260907b → 20260907-verified. После каждого переключения подтверждены HTTPS, readiness и SHA. Это проверка конфигурации и артефактов на одном проверенном SHA; обратная миграция БД не выполнялась.

## Стек и роль

- **Python / FastAPI**: Отдельный публичный runtime, лимиты и HTTP-контракты.
- **trafilatura / BeautifulSoup**: Реальное извлечение текста из встроенного HTML.
- **scikit-learn**: TF-IDF + LogisticRegression с фиксированным random_state.
- **PostgreSQL**: Сессионные результаты pipeline в JSONB, TTL и атомарная квота.

## Code-map

|Функция|Экран / API|Модуль|Данные / проверка|
|---|---|---|---|
|Выбор материала|форма / GET api/runs|portfolio_web/app.js, portfolio_pipeline.CORPUS|синтетический корпус; production browser QA|
|Очистка|POST api/runs|content/processor.processed_values|text_hash, язык, признаки; test_content_utils.py|
|Извлечение|POST api/runs|content/link_enricher.extract_html|текст HTML; test_portfolio_pipeline.py|
|Классификация|результат|content/classifier.predict|TF-IDF модель; test_portfolio_pipeline.py|
|Черновик|результат|content/rewriter.draft_from_template|portfolio_runs.result; test_portfolio_pipeline.py|
|Повтор и сброс|POST retry / DELETE runs|portfolio.py|только своя session_id; production API isolation QA|

## Схемы

- Компоненты опубликованного демо — `diagrams/components.mmd`
- Основной сценарий — `diagrams/scenario.mmd`
- Размещение опубликованной версии — `diagrams/deployment.mmd`
- Состав данных и границы хранения — `diagrams/data.mmd`
- Исходный интеграционный контур — не развёрнут в демо — `diagrams/original.mmd`

## Индекс изображений

- 01-overview: Назначение, стек и точные границы моков; 2026-09-06T23:52:32.452Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 02-input: Собственный материал, переключатель контролируемого сбоя и пустой результат; 2026-09-06T23:52:32.961Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 03-pipeline: Четыре реально выполненных этапа с измеренным временем; 2026-09-06T23:52:36.019Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 04-classification: TF-IDF классификатор и явное ограничение учебной модели; 2026-09-06T23:52:36.535Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 05-draft: Шаблонный редакторский черновик с источником; 2026-09-06T23:52:37.065Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 06-technical-result: Структурированный результат обработки и происхождение полей; 2026-09-06T23:52:37.645Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 07-failure: Явно смоделированный сбой извлечения без внешнего запроса; 2026-09-06T23:52:38.617Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 08-retry: Повтор исправляет тот же материал; новая запись не создаётся; 2026-09-06T23:52:39.284Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 09-education: Другой материал: учебный практикум и его черновик; 2026-09-06T23:52:40.007Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 10-source: Собственный HTML-источник с явной синтетической природой; 2026-09-06T23:52:40.189Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 11-mobile-overview: Мобильная версия с обозначением моков; 2026-09-06T23:52:40.774Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 12-mobile-pipeline: Мобильные этапы и извлечённые признаки; 2026-09-06T23:52:41.257Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548
- 13-reset: Сброс только своей истории и возвращение в пустое состояние; 2026-09-06T23:52:41.972Z; SHA b13048fbc5490d161d7a89ad9c72b4b74bafc548

## Подробности runtime из репозитория

# re-publisher: публичный демонстрационный сценарий

Поддомен: https://publisher.komaroff-dev.ru/. Подготовка демо: 7 сентября 2026.
Дата подготовки не является датой начала разработки исходного проекта.

## Что выполняется

`app.portfolio` — отдельная точка входа. Операционный admin и Telegram collector не монтируются.
Три собственных материала проходят исходные `processed_values`, `extract_html`,
`build_classification_text`, `predict` и `draft_from_template`.
HTML читается из строки, а не загружается по пользовательскому адресу.
Измерения этапов снимаются `perf_counter` на сервере и не являются внешним бенчмарком.

Telegram input заменён фиксированным корпусом, LLM — существующим шаблонным редактором.
TF-IDF + LogisticRegression работают реально. Модель обучается на шести синтетических
примерах с фиксированным random_state. Числа классификатора не характеризуют качество
на независимой выборке. Внешняя публикация, shell, Codex, платные API и загрузки отключены.

## Хранилище и границы

Общий PostgreSQL, отдельные БД/пользователь publisher. Только таблица `portfolio_runs`
с результатами демонстрации; рабочая схема исходного collector не разворачивается.
Подписанная cookie Secure/HttpOnly/SameSite=Lax, срок один час. В БД хранится SHA-256
сессии. Все чтения, повтор и сброс ограничены этой сессией. Очистка каждые пять минут.
Лимиты: 20 материалов на сессию, 1000 всего, два места выполнения. Общий лимит
проверяется под PostgreSQL advisory lock. Разрешены только три fixture ID.
Повтор сбоя обновляет тот же материал, не создавая новую запись.

## Code-map

| Функция | Экран / API | Модуль | Данные | Проверка |
|---|---|---|---|---|
| Выбор материала | форма / GET api/runs | portfolio_web/app.js, portfolio_pipeline.CORPUS | синтетический корпус | production browser QA |
| Очистка | POST api/runs | content/processor.processed_values | text_hash, язык, признаки | test_content_utils.py |
| Извлечение | POST api/runs | content/link_enricher.extract_html | текст HTML | test_portfolio_pipeline.py |
| Классификация | результат | content/classifier.predict | TF-IDF модель | test_portfolio_pipeline.py |
| Черновик | результат | content/rewriter.draft_from_template | portfolio_runs.result | test_portfolio_pipeline.py |
| Повтор и сброс | POST retry / DELETE runs | portfolio.py | только своя session_id | production API isolation QA |

Перед изменением проследить функцию от экрана до модуля и хранилища. После изменения
актуализировать карту, схемы и связанные сценарии. Исходный сборщик и публичное демо
имеют разные схемы развёртывания; нельзя выдавать демонстрационную таблицу за рабочую БД.

## Сборка и эксплуатация

Сборка `docker build -f Dockerfile.portfolio -t portfolio-publisher:VERSION .` вне VPS.
Python base закреплён digest, полный набор зависимостей — requirements.portfolio.lock.
Запуск: DB_DSN и PORTFOLIO_SECRET передаются окружением, секреты не включаются в образ.
384 MiB RAM, 0.7 CPU, healthcheck /healthz проверяет БД, Docker logs 3×5 MB.
Нет публичных портов БД. Nginx проксирует localhost:18404.
Workflow по умолчанию заменён ручной проверкой без SSH, prune и удаления логов сервера.
