[_PSSR_drafts] Описание и ТЗ PSSR MVP.docx
Сущности
PSSR MVP<br>
Итоговое описание и базовые ТЗ<br>
Часть 1. Архитектура, границы ответственности, репозитории<br>
1. Назначение системы<br>
PSSR MVP это локальная прототипная система, которая принимает события из разнородных источников, детерминированно определяет режим и контур, применяет гейты L0 и L-Law как допуск, фиксирует аудитный след и возвращает результат в форме карточки решения. Система допускает использование ИИ в модулях, где это не критично для допуска, но ускоряет работу оператора, повышает качество разметки и помогает готовить тексты. На текущей фазе система работает на одном локальном компьютере, без требований к промышленной информационной безопасности и сертификации, но с сохранением архитектурной дисциплины, чтобы дальнейшее усиление безопасности не ломало модель.<br>
Ключевое требование MVP это воспроизводимость. При одинаковом входном событии и одинаковой версии реестра правил результат определения режима, гейтов и допустимых действий должен быть одинаковым. Это не означает “идеальную истинность”, но означает, что система не будет непредсказуемой.<br>
2. Базовые принципы<br>
Принцип единственного исполнителя означает, что только Kernel формирует решения и допускает или блокирует дальнейшие действия. Ни UI, ни коннекторы, ни AI Assist не могут обходить Kernel и не могут производить “выходной текст” напрямую. Это защищает систему от деградации в “набор скриптов” и обеспечивает аудит.<br>
Принцип разделения данных означает, что сырьё и артефакты обработки не смешиваются. Сырьё это то, что пришло извне, с минимальными преобразованиями. Артефакты это то, что система вывела и зафиксировала как собственное действие, включая режим, контур, нарушения, протокол и черновики. Разделение нужно для дедупликации, расследований и доказуемости.<br>
Принцип “ИИ как ускоритель, не как допуск” означает, что ИИ может резюмировать, извлекать сущности, группировать события, переводить и готовить черновики. Но ИИ не имеет права определять режим, снимать ограничения L0 или L-Law и подтверждать факты как “verified” автоматически. Любой AI-draft, который потенциально может стать внешней формулировкой, обязан пройти повторную проверку через гейты Kernel.<br>
Принцип контрактов означает, что все компоненты системы общаются через зафиксированные схемы и OpenAPI, находящиеся в отдельном репозитории contracts. Контракты являются источником истины, а не “договорённостью на словах”.<br>
3. Компонентная архитектура<br>
Система состоит из пяти независимых компонент.<br>
Kernel. Единственный исполнитель решения. Принимает события, записывает сырьё, связывает события, определяет режим и контур по реестру, применяет гейты, формирует решение, пишет журнал, отдаёт карточку решения и агрегаты для UI.<br>
AI Assist. Отдельный сервис помощника. Выполняет суммаризацию, извлечение сущностей, перевод, кластеризацию и генерацию черновиков. Работает только как advisory. Результаты сохраняются в Kernel через специальный API, чтобы у системы было одно хранилище истины.<br>
Ingest. Набор коннекторов источников. Каждый коннектор преобразует данные конкретного источника к единой Event Schema и отправляет в Kernel. Ingest хранит только checkpoint и технические состояния, но не хранит решения.<br>
UI. Отдельный фронтенд. Реализует три представления: Mobile Console, Desktop Operator, Sitcenter Wall. Работает только через Kernel API. Не содержит логики допуска.<br>
Stack Orchestrator. Отдельный репозиторий для сборки локального запуска, docker-compose, конфигурации окружения и тестовых сценариев.<br>
4. Организация репозиториев и выпусков<br>
Мы фиксируем multi-repo как базовую организацию.<br>
Репозиторий pssr-contracts. Здесь лежат JSON Schema объектов и OpenAPI спецификация Kernel. Здесь же определены enum режимов, контуров, статусов и типы событий. В репозитории есть фиксированные примеры событий и решений для тестов, и генерация артефактов для Python и Node. Контракты имеют версионирование. Любое изменение контрактов идёт через повышение версии и проверку совместимости.<br>
Репозиторий pssr-kernel. Здесь только Kernel, его доменная логика, миграции SQLite, тесты воспроизводимости, и реализация OpenAPI. Kernel не содержит ни UI, ни конкретных парсеров YouScan, ни “бизнес-логики источников”. Kernel имеет один файловый каталог артефактов и одну БД. Это специально, чтобы не появилось “двух правд”.<br>
Репозиторий pssr-assist. Здесь AI Assist как отдельный HTTP сервис. Он читает данные через Kernel API, генерирует артефакты и возвращает их в Kernel через API сохранения. Assist может использовать Ollama локально. Он не имеет прямого доступа к БД Kernel.<br>
Репозитории pssr-ingest-youscan, pssr-ingest-rss, pssr-ingest-import. Каждый коннектор отдельно, чтобы не связывать циклы релизов. Все коннекторы используют общий Python пакет из contracts и отправляют события только в Kernel. В каждом коннекторе есть строгая логика checkpoint: checkpoint обновляется только при успешной обработке событий Kernel.<br>
Репозиторий pssr-ui. Отдельный фронтенд на стандартном стеке и Bootstrap 5. Фронт получает типы из contracts. Реализует три view-режима как отдельные маршруты и отдельные layout’ы. Обновление очереди и ситцентра делается через SSE или polling fallback.<br>
Репозиторий pssr-stack. В этом репозитории нет доменной логики. Здесь docker-compose, переменные окружения, локальные volume, и документация “как поднять всё локально”. Здесь же могут лежать таблицы тестовых сценариев tabletop, чтобы прогонять ядро на входных наборах.<br>
5. Принципиальные границы ответственности между компонентами<br>
Kernel обязан обеспечить, чтобы любое решение существовало только как запись в его журнале. Если запись не сделана, решение не возвращается. Это фундаментальная гарантия. Kernel обязан фиксировать версию реестра в каждом решении.<br>
UI обязан отображать режим и контур как доминирующий сигнал, а hard_stop как явное ограничение. UI не может “скрывать” блокировки и не может предлагать действия, которых Kernel не разрешил через next_actions.<br>
Ingest обязан быть идемпотентным на уровне источника. Повторный запуск коннектора не должен создавать дублей. Для этого коннектор хранит checkpoint, а Kernel хранит fingerprint и индексы.<br>
AI Assist обязан маркировать все результаты как advisory и не должен производить “готовый текст” без повторного gate-check. Любой draft должен быть возвращён в Kernel и проверен, прежде чем UI получит его как “approved draft”.<br>
Contracts обязаны оставаться назад-совместимыми как минимум в пределах minor версии. Kernel обязан оставаться назад-совместимым по API хотя бы на период активной разработки MVP, иначе вы будете постоянно чинить UI и ingest вместо развития логики.<br>
6. Выбор UI стека и роль Bootstrap<br>
Вы просили заранее заложить стандартные и обкатанные решения. В этой архитектуре Bootstrap 5 фиксируется как базовая дизайн-система. Это означает, что UI проектируется вокруг стандартных компонентов Bootstrap, а кастомные элементы допускаются только в части визуальной индикации режима и hard_stop. Это дисциплинирует интерфейс, ускоряет разработку и снижает риск “UI ради UI”.<br>
Так как UI отдельный, опыт показывает, что в дальнейшем sitcenter-экран почти всегда начинает жить по своим законам. Поэтому Sitcenter Wall фиксируется как отдельный маршрут и отдельный layout, а не как “responsive-верстка на тех же компонентах”. Адаптивность используется как техническая мера, но логика отображения различается.<br>
7. Сводка ключевых требований MVP на уровне архитектуры<br>
Система должна работать локально, но уже в “правильной” компонентной архитектуре. Kernel должен быть единственным исполнителем решений, а гейты должны быть обязательными. Сырьё и решения разделены. Контракты вынесены отдельно. UI поддерживает телефон, desktop и ситцентр не через попытку “одним экраном”, а через три режима отображения. ИИ ускоряет работу, но не принимает решений и не снимает ограничения.<br>
Часть 2. Контракты, API, данные, Router/Gates, next_actions<br>
8. Контракты и версии<br>
Репозиторий pssr-contracts является источником истины для объектов и интерфейсов. В MVP контракты включают как минимум:<br>
JSON Schema: Event, Decision, AIArtifact, RegistryRule, RegistryTrigger, RegistryProtocol<br>
OpenAPI: Kernel API и Assist API<br>
Enum: source_type, mode_code, contour_code, severity, decision_status, artifact_type<br>
Набор фикстур: примеры входных событий и ожидаемых решений для тестов<br>
Версионирование контрактов. Вводится semver. Изменения, которые ломают обратную совместимость, идут только через major. Kernel и UI обязаны проверять совместимость на CI: если контракт обновился, сборка должна упасть, если сервис не соответствует схеме.<br>
9. Канонические перечисления<br>
9.1. SourceType<br>
social_listening<br>
news<br>
internal<br>
manual<br>
9.2. ModeCode<br>
N0<br>
N1<br>
N2<br>
N3<br>
N4<br>
9.3. ContourCode<br>
A<br>
S<br>
C<br>
D<br>
9.4. Severity<br>
CRITICAL<br>
HIGH<br>
MEDIUM<br>
LOW<br>
9.5. DecisionStatus<br>
APPROVED<br>
BLOCKED<br>
9.6. ArtifactType<br>
summary<br>
entities<br>
translation<br>
cluster<br>
draft<br>
sitcenter_brief<br>
test_case<br>
10. Контракт Event<br>
Event является входным объектом для Kernel. Минимальный объект, который обязан быть принят системой, это source_type, source_name, text. Остальные поля либо заполняются ingest-коннектором, либо проставляются Kernel.<br>
Схема Event фиксируется так, чтобы она была стабильной для всех источников.<br>
{<br>
"type": "object",<br>
"required": ["source_type", "source_name", "text"],<br>
"properties": {<br>
"event_id": { "type": ["string", "null"] },<br>
"source_type": { "type": "string", "enum": ["social_listening","news","internal","manual"] },<br>
"source_name": { "type": "string" },<br>
"source_item_id": { "type": ["string","null"] },<br>
"observed_at": { "type": ["string","null"] },<br>
"received_at": { "type": ["string","null"] },<br>
"url": { "type": ["string","null"] },<br>
"language": { "type": "string", "enum": ["ru","kk","en","unknown"], "default": "unknown" },<br>
"title": { "type": ["string","null"] },<br>
"text": { "type": "string", "minLength": 1 },<br>
"author": { "type": ["string","null"] },<br>
"reach_proxy": { "type": ["number","null"] },<br>
"sentiment_proxy": { "type": ["number","null"] },<br>
"tags": { "type": "array", "items": { "type": "string" } },<br>
"raw_ref": { "type": ["string","null"] },<br>
"meta": { "type": "object", "additionalProperties": true }<br>
},<br>
"additionalProperties": false<br>
}<br>
Требование. Kernel обязан проставлять received_at. Если observed_at отсутствует, Kernel проставляет observed_at равным received_at и добавляет метку в meta.<br>
11. Контракт Decision<br>
Decision является выходным объектом Kernel для любого обработанного Event. Decision обязателен даже если событие заблокировано. Decision является “атомом” обсуждения, аудита и привязки UI.<br>
{<br>
"type": "object",<br>
"required": ["decision_id","created_at","registry_version","mode_code","contour_code","hard_stop","status","violations","reasons","next_actions","links"],<br>
"properties": {<br>
"decision_id": { "type": "string" },<br>
"created_at": { "type": "string" },<br>
"registry_version": { "type": "integer" },<br>
"mode_code": { "type": "string", "enum": ["N0","N1","N2","N3","N4"] },<br>
"contour_code": { "type": "string", "enum": ["A","S","C","D"] },<br>
"hard_stop": { "type": "boolean" },<br>
"status": { "type": "string", "enum": ["APPROVED","BLOCKED"] },<br>
"violations": {<br>
"type": "array",<br>
"items": {<br>
"type": "object",<br>
"required": ["rule_code","rule_type","severity","description"],<br>
"properties": {<br>
"rule_code": { "type": "string" },<br>
"rule_type": { "type": "string", "enum": ["L0","LLAW"] },<br>
"severity": { "type": "string", "enum": ["CRITICAL","HIGH","MEDIUM","LOW"] },<br>
"description": { "type": "string" }<br>
}<br>
}<br>
},<br>
"reasons": { "type": "array", "items": { "type": "string" } },<br>
"next_actions": { "type": "array", "items": { "type": "string" } },<br>
"links": {<br>
"type": "object",<br>
"required": ["raw_item_id","canonical_event_id"],<br>
"properties": {<br>
"raw_item_id": { "type": "integer" },<br>
"canonical_event_id": { "type": "integer" }<br>
}<br>
},<br>
"debug": { "type": ["object","null"], "additionalProperties": true }<br>
},<br>
"additionalProperties": false<br>
}<br>
Требование. decision_id возвращается только если запись решения успешно сохранена в БД. Если запись не сохранена, Kernel отвечает ошибкой.<br>
12. Контракты Registry объектов<br>
Правила и триггеры должны быть внешними по отношению к коду, то есть храниться в реестре и версионироваться.<br>
RegistryRule:<br>
rule_code<br>
rule_type (L0 или LLAW)<br>
severity<br>
description<br>
patterns<br>
is_active<br>
RegistryTrigger:<br>
mode_code<br>
contour_code (опционально)<br>
priority<br>
patterns<br>
is_active<br>
RegistryProtocol:<br>
protocol_code<br>
name<br>
allowed_modes<br>
constraints (включая стиль и запреты)<br>
is_active<br>
В MVP допускается, что RegistryProtocol в начале будет содержать только next_actions по режимам и минимальные текстовые ограничения, а позже будет расширяться.<br>
13. Kernel API (OpenAPI смысловые эндпойнты)<br>
13.1. Обработка события<br>
POST /event<br>
Вход: Event<br>
Выход: Decision<br>
Гарантии:<br>
записывается raw_item<br>
создаётся или выбирается canonical_event<br>
вычисляется режим и контур<br>
применяются гейты<br>
записывается decision<br>
возвращается Decision<br>
13.2. Получение решения<br>
GET /decision/{decision_id}<br>
Выход: Decision + расширенная карточка для UI, включая нормализацию и ссылки на связанные raw_items, если запрошено параметрами.<br>
Для MVP можно сделать два режима:<br>
краткий (по умолчанию)<br>
полный (include=details)<br>
13.3. Очередь<br>
GET /queue?limit=50&mode=N2&status=BLOCKED<br>
Выход: массив элементов очереди. Каждый элемент содержит минимум:<br>
decision_id<br>
created_at<br>
mode_code<br>
contour_code<br>
status<br>
source_name<br>
короткий preview текста (первые N символов)<br>
hard_stop<br>
13.4. Sitcenter агрегаты<br>
GET /sitcenter/summary<br>
Выход:<br>
текущий режим системы (как агрегат по последним решениям или как “последнее состояние”)<br>
количество событий по режимам за период<br>
количество blocked за период<br>
топ причин блокировок<br>
последние решения N3/N2<br>
13.5. Sitcenter поток<br>
GET /sitcenter/stream (SSE)<br>
Выход: события обновления очереди и агрегатов, чтобы большой экран обновлялся без перезагрузки.<br>
13.6. Реестр<br>
GET /registry/rules<br>
POST /registry/rules<br>
GET /registry/triggers<br>
POST /registry/triggers<br>
GET /registry/protocols<br>
POST /registry/protocols<br>
Требование. Любое изменение реестра увеличивает registry_version и фиксируется в журнале реестра (в MVP можно журналировать изменения как отдельную таблицу или через системные decisions типа “REGISTRY_UPDATE”).<br>
14. Kernel: хранение и транзакционность<br>
Kernel использует SQLite и файловое хранилище артефактов.<br>
Требование. Обработка /event обязана быть транзакционной минимум на уровне: raw_item + canonical_event link + decision. Если что-либо не записалось, decision не возвращается.<br>
15. Формальная спецификация нормализации и fingerprint<br>
Нормализация для fingerprint должна быть:<br>
воспроизводимой<br>
консервативной<br>
не зависящей от внешних библиотек, которые могут менять поведение между версиями без явного контроля<br>
MVP нормализация:<br>
lowercasing<br>
удаление двойных пробелов<br>
замена табов на пробелы<br>
удаление невидимых символов<br>
обрезка до разумного лимита (например 20 000 символов) с фиксацией в meta, если было обрезание<br>
Fingerprint:<br>
hash(normalized_text + “|” + source_name + “|” + (source_item_id or “-”) + “|” + (url or “-”))<br>
Важное уточнение. fingerprint не является “истиной” дедупликации, это практическая эвристика. Поэтому система хранит и fingerprint, и source_item_id, и url. Это позволяет позже улучшать дедуп без потери данных.<br>
16. Router: формальная спецификация<br>
Router использует registry_triggers. Алгоритм Router в MVP детерминированный и основан на pattern matching.<br>
Псевдокод:<br>
candidates = []<br>
for t in active registry_triggers:<br>
if matches_any(t.patterns, normalized_text):<br>
candidates.append(t)<br>
if empty:<br>
mode = "N0"<br>
contour = default_contour("N0")<br>
else:<br>
max_pr = max(c.priority for c in candidates)<br>
top = [c for c in candidates if c.priority == max_pr]<br>
if len(top) == 1:<br>
chosen = top[0]<br>
else:<br>
chosen = tie_break(top)<br>
mode = chosen.mode_code<br>
contour = chosen.contour_code or default_contour(mode)<br>
Tie-break фиксируется в коде и тестируется. Tie-break правило: выбирается более жёсткий режим по порядку N4 > N3 > N2 > N1 > N0. Если режим одинаковый, выбирается тот, где contour указан явно. Если и это одинаково, выбирается первый по стабильной сортировке (например по id триггера).<br>
Default contour фиксируется в реестре или в коде, но должен быть единственным источником истины. Для MVP допустимо зафиксировать в коде. Позже лучше вынести в registry_protocols.<br>
17. Gates: формальная спецификация<br>
Гейт L-Law имеет приоритет. Если срабатывает LLAW правило, то hard_stop устанавливается в true и статус решения становится BLOCKED независимо от L0.<br>
Псевдокод:<br>
violations = []<br>
if matches_any(LLAW.patterns):<br>
violations += LLAW violations<br>
hard_stop = true<br>
status = BLOCKED<br>
next_actions = next_actions_for_hard_stop(mode)<br>
return<br>
if matches_any(L0.patterns):<br>
violations += L0 violations<br>
hard_stop = false<br>
status = BLOCKED<br>
next_actions = next_actions_for_l0_block(mode)<br>
return<br>
hard_stop = false<br>
status = APPROVED<br>
next_actions = next_actions_for_mode(mode)<br>
Важное уточнение. Даже при APPROVED next_actions ограничивают то, что UI имеет право предложить оператору. APPROVED не означает “публикация разрешена”, это означает “событие допускается к дальнейшим разрешённым операциям”.<br>
18. Next_actions: спецификация<br>
Next_actions являются ключевым мостом между логикой Kernel и UX. Они должны быть достаточно грубыми, чтобы не зависеть от конкретного UI, но достаточно конкретными, чтобы UI мог ограничить сценарии.<br>
В MVP фиксируем минимальный набор действий как строковые коды.<br>
Базовые действия:<br>
ACTION_LOG_ONLY (зафиксировать и оставить в мониторинге)<br>
ACTION_REQUEST_VERIFICATION (пометить UNVERIFIED и запросить подтверждение)<br>
ACTION_ESCALATE (эскалация оператору/руководителю)<br>
ACTION_PREPARE_BRIEF (подготовить краткую сводку)<br>
ACTION_PREPARE_DRAFT (подготовить черновик формулировки)<br>
ACTION_USE_TEMPLATE (использовать шаблон, без свободного текста)<br>
ACTION_FREEZE_OUTPUT (запрет на любые внешние тексты)<br>
Правило по режимам в MVP:<br>
В N0 обычно доступны LOG_ONLY, REQUEST_VERIFICATION, PREPARE_DRAFT, PREPARE_BRIEF.<br>
В N2 обычно доступны REQUEST_VERIFICATION, ESCALATE, PREPARE_BRIEF, PREPARE_DRAFT с ограничениями.<br>
В N3 доступны ESCALATE, PREPARE_BRIEF, USE_TEMPLATE, FREEZE_OUTPUT.<br>
При hard_stop всегда доступен FREEZE_OUTPUT, и недоступен PREPARE_DRAFT.<br>
Детальные правила и связка с протоколами должны быть в registry_protocols. В MVP можно захардкодить матрицу next_actions в Kernel и позже вынести в реестр, но при этом необходимо зафиксировать её как часть версии Kernel и покрыть тестами.<br>
Часть 3. AI Assist, коннекторы, UI, CI/CD, критерии приёмки<br>
19. AI Assist: архитектура и API<br>
AI Assist реализуется как отдельный сервис pssr-assist. Его назначение — ускорять и улучшать качество работы оператора без вмешательства в логику допуска.<br>
Assist не имеет прямого доступа к БД Kernel. Все операции сохранения происходят через API Kernel.<br>
19.1. Поток взаимодействия<br>
Сценарий “AI summary”:<br>
UI запрашивает у Assist summary по canonical_event_id.<br>
Assist запрашивает через Kernel API все raw_items данного canonical_event.<br>
Assist генерирует summary.<br>
Assist отправляет в Kernel POST /ai/artifact.<br>
Kernel сохраняет ai_artifact.<br>
UI получает результат через Kernel.<br>
Сценарий “AI draft”:<br>
UI запрашивает draft по decision_id и protocol_code.<br>
Assist запрашивает через Kernel API:<br>
decision<br>
режим<br>
протокол<br>
Assist генерирует draft.<br>
Assist отправляет draft в Kernel POST /ai/draft-check.<br>
Kernel выполняет повторный gate-check.<br>
Kernel сохраняет draft как ai_artifact с gate_result.<br>
Kernel возвращает статус draft: APPROVED или BLOCKED.<br>
UI отображает draft только с указанием результата проверки.<br>
Assist не имеет права напрямую возвращать draft в UI без прохождения через Kernel.<br>
19.2. Assist API (минимальный)<br>
POST /assist/summary<br>
Вход:<br>
canonical_event_id<br>
POST /assist/entities<br>
Вход:<br>
raw_item_id<br>
POST /assist/draft<br>
Вход:<br>
decision_id<br>
protocol_code<br>
Assist API является внутренним. В продакшене его может вызывать только UI или оркестратор.<br>
19.3. Ограничения AI Assist<br>
Не может менять режим.<br>
Не может менять hard_stop.<br>
Не может менять registry_version.<br>
Не может напрямую писать в БД Kernel.<br>
Все результаты помечаются как advisory.<br>
20. Коннекторы: формализация<br>
Коннектор — отдельный сервис, который:<br>
получает данные из источника,<br>
приводит к Event Schema,<br>
отправляет в Kernel.<br>
Коннектор обязан быть идемпотентным.<br>
20.1. Общие требования к коннекторам<br>
Наличие checkpoint (last_processed_id или timestamp).<br>
Checkpoint обновляется только после успешного ответа Kernel.<br>
Повторный запуск не создаёт дублей.<br>
При ошибке Kernel коннектор логирует и повторяет попытку.<br>
Коннектор не хранит решений.<br>
20.2. YouScan Connector<br>
Функция:<br>
получать mentions через API<br>
маппить в Event Schema<br>
отправлять в Kernel<br>
Дополнительные поля:<br>
reach_proxy<br>
sentiment_proxy<br>
platform<br>
engagement<br>
Checkpoint:<br>
хранится локально (файл или SQLite)<br>
обновляется после успешной обработки партии<br>
20.3. RSS Connector<br>
Функция:<br>
регулярный polling RSS<br>
hash URL+title<br>
отправка в Kernel<br>
Дедупликация:<br>
если URL уже обработан, событие не отправляется повторно<br>
20.4. Manual Import Connector<br>
Функция:<br>
загрузка файла (CSV/JSON)<br>
преобразование строк в Event<br>
отправка в Kernel<br>
Используется для tabletop-тестов.<br>
21. UI: детальная спецификация<br>
UI реализуется как отдельное SPA или веб-приложение с Bootstrap 5.<br>
21.1. Mobile Console<br>
Экран:<br>
список последних решений<br>
карточка выбранного события<br>
режим (крупно)<br>
статус (APPROVED/BLOCKED)<br>
кнопки next_actions<br>
Особенности:<br>
минимальное количество текста<br>
collapsible детали<br>
крупная индикация hard_stop<br>
21.2. Desktop Operator<br>
Экран:<br>
очередь<br>
карточка решения<br>
блок violations<br>
блок next_actions<br>
блок AI artifacts<br>
ссылка на Decision Viewer<br>
21.3. Sitcenter Wall<br>
Экран:<br>
текущий режим (очень крупно)<br>
индикатор hard_stop<br>
количество событий по режимам<br>
последние решения N2/N3<br>
топ нарушений<br>
Особенности:<br>
read-only<br>
обновление через SSE<br>
минимальное взаимодействие<br>
22. CI/CD и контроль качества<br>
Каждый репозиторий имеет свой CI:<br>
contracts: проверка схем<br>
kernel: unit-тесты Router и Gates<br>
assist: тест генерации и возврата draft<br>
ingest: тест идемпотентности<br>
ui: типизация и проверка контрактов<br>
Интеграционные тесты:<br>
запуск всего стека через docker-compose<br>
отправка тестовых событий<br>
проверка, что режимы совпадают с ожиданиями<br>
проверка, что blocked события не дают draft<br>
23. Критерии приёмки MVP<br>
Kernel:<br>
Детерминированный режим.<br>
L-Law блокирует корректно.<br>
registry_version фиксируется.<br>
Нет решения без decision_id.<br>
Assist:<br>
Генерирует summary.<br>
Draft проходит повторную проверку.<br>
Заблокированный draft отображается как BLOCKED.<br>
Ingest:<br>
Нет дублей при повторном запуске.<br>
Checkpoint работает.<br>
UI:<br>
Отображает режим.<br>
Отображает hard_stop.<br>
Не предлагает запрещённых действий.<br>
Sitcenter:<br>
Обновляется автоматически.<br>
Показывает агрегаты корректно.<br>
24. Итоговое состояние MVP<br>
В результате вы получаете:<br>
раздельные сервисы,<br>
воспроизводимое ядро,<br>
масштабируемую архитектуру,<br>
возможность расширять источники,<br>
возможность усиливать безопасность позже,<br>
возможность заменить фронтенд без переписывания Kernel.