diff --git a/AGENTS.md b/AGENTS.md index 541634f..13e5011 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,7 @@ way; this file is *how to work in them*. | talks, opinions, fights, romance pack | [`docs/design/09-social/`](docs/design/09-social/social.md) — slice 9, phases 41–47 | | lesson consequences (teacher present, skill, warmth, textbook, weather commute) | [`docs/design/off-queue/lesson-consequences.md`](docs/design/off-queue/lesson-consequences.md) — phases 48, 50–53 | | layer tests, client tsc-in-test, worker/card splits, dump nodes | [`docs/design/10-craft/`](docs/design/10-craft/craft.md) — slice 10, phases 56–59 | +| notices, toasts, scene prompt contributions, Krea-only catalog | [`docs/design/11-events/`](docs/design/11-events/events.md) — slice 11, phases 63–67 | | a bug in already-shipped behaviour | `.claude/skills/bug-work` — document in `docs/bugs/`, discuss only if the fix has a fork, then patch | | a task that is not in any slice | `.claude/skills/side-work` — discuss, document in [`docs/phases/off-queue/`](docs/phases/off-queue/README.md), then implement | | a new named slice of the game | `.claude/skills/slice-work` — discuss, write design and phases, then `/phase-orch` | diff --git a/docs/design/11-events/events.md b/docs/design/11-events/events.md new file mode 100644 index 0000000..9b20dc3 --- /dev/null +++ b/docs/design/11-events/events.md @@ -0,0 +1,205 @@ +# События и сцена + +Договорённость на срез после портретов (60–62) и ремесла, не текущий код. +Портреты: [`portrait-models.md`](../off-queue/portrait-models.md), [`portrait-loras-embeds.md`](../off-queue/portrait-loras-embeds.md), [`portrait-prompt-craft.md`](../off-queue/portrait-prompt-craft.md). +Экран: [`near-term.md`](../01-shell/near-term.md). Defs: [`defs.md`](../01-shell/defs.md). Сессия (хозяин / гость): [`session.md`](../08-session/session.md). +Погода и вещи: [`inventory.md`](../07-inventory/inventory.md). Протокол: [`protocol.md`](../../protocol.md). + +Мир начинает **говорить** в две стороны: игроку — тостом или варнингом; генератору — коротким +вкладом в промпт, если объект реально в кадре. Подписка — не общая C# шина на весь раствор. +Симуляция поднимает факт, работник складывает уведомление, клиент рисует. Сборщик кадра +**спрашивает комнату**, а не слушает шину. + +Срез отвечает на вопросы «что только что случилось», «почему часы встали» и «почему в промпте +снег и доска». Оценок, хроники за год, арта локации на карте и LLM нет. + +## Что увидит игрок + +В углу — короткое info-сообщение (утро, звонок на урок): само гаснет, клик закрывает раньше, +часы идут. Варнинг и ошибка ставят паузу; Play не включает часы, пока карточка не закрыта. +На части карточек — только текст; на части — кнопка «создать картинку» (портрет человека из +события, уже с вкладом сцены). + +Новая школа рисует только Krea 2. «Показать промпт» может содержать комнату, погоду и вещи +с непустым вкладом — не список всех стульев. + +## На какие вопросы отвечает + +- Как факт симуляции доходит до UI, не нарушая инварианты 1, 3, 4 и 7. +- Какие уведомления паузат школу и переживают F5. +- Как почти любой def может добавить позитив/негатив/LoRA/embed в кадр, не раздув промпт. +- Чем новая школа генерирует после отказа от SDXL. + +## Чего нет + +- Третьей колонки-ленты на весь учебный год. Тосты и модал — не журнал. +- Отдельного kind `scene` (коридор без человека). Кадр по-прежнему человек. +- ControlNet, позы в UI, перегенерации старых PNG. +- Общего `EventBus` во всех сборках. Симуляция не знает Swarm и тосты. +- Автогенерации всех лиц. LLM. Звука. Лифта живых `portraitSettings`. +- Отдельной SDXL-ветки промпта: ванильный каталог — только Krea 2. + +## Что меняется в уже принятом + +| Было | Стало | Почему | +| --- | --- | --- | +| Отдельной панели событий нет ([`near-term.md`](../01-shell/near-term.md)) | Оверлей: info-тост и модал warning/error | Ленты всё ещё нет; пустая колонка не возвращается | +| «Общая лента школы — не этот срез» ([`social.md`](../09-social/social.md)) | Уведомления есть; хроники за год нет | Игрок видит звонок и аварию генерации, не дневник | +| Каталог Swarm: SDXL + Muse + Realism v2.5; child — DreamShaper | Только Muse и Realism v2.5; child тоже v2.5 | Krea 2 — не SDXL; «только Krea» значит без Lightning/DreamShaper | +| Промпт: модель → стиль → кадр → extra → поза → внешность → одежда | Плюс вклад сцены после одежды, с бюджетом | Стул без текста молчит; доска и снег могут сказать фразу | +| LoRA/embed только модель/пресет/кадр | Плюс вклад вещей и комнаты в том же кадре, тоже с бюджетом | Вещь может принести свою сеть, не меняя пресет школы | +| Play всегда включает часы у хозяина | Пока висит pausing-уведомление, `SetRunning(true)` игнорируется | Варнинг должен остановить игру, а не проситься в углу | +| Гость только смотрит часы | Гость видит те же уведомления, не закрывает pausing и не жмёт «создать картинку» | Как пауза и портрет: хозяин | + +Протокол: новый кадр уведомления и намерение закрыть. Версию сокета бампит **коммит фазы 64**, не этот документ. HTTP: dismiss и генерация с уведомления. Сейв: очередь незакрытых warning/error. + +## Этап A — только Krea 2 + +Из `swarmui.json` уходят четыре SDXL, в том числе DreamShaper у `child`. Остаются +`museByStableYogi_v30TurboGguf.gguf` и `realismByStableYogi_v25INT8Turbo.safetensors` +(8 / CFG 1 / euler / simple). Пресет `default` и `child` — Realism v2.5; стиль и кадр +`child` — проза без `(word:1.4)`. Пикер по-прежнему Swarm ∩ каталог. + +Живые школы **не лифтим**: копия в сейве как после фазы 62. Новая школа не видит babes / +epicrealism / lustify / DreamShaper, даже если они ещё лежат в Swarm. + +После этапа школа целая: портреты рисуются, сцена в промпт ещё не вкладывается. + +## Этап B — лента + +### Факт и уведомление + +Новый вид `EventDef`. Триггер — короткое перечисление, не скрипт: + +- `dayStart` — рабочее утро (тот же край, что утренняя одежда / семейное); +- `lessonStart` — звонок на урок, **один** на школу, не по классам; +- `generationFailed` — Swarm не нарисовал (не симуляция: сервис портретов). + +Системы отдают факт тем же швом, что `AffinityEvent`: список, без UI. Работник сопоставляет +`EventDef`, кладёт `Notice` на школу, шлёт кадр в исходящие. Симуляция не ссылается на ASP.NET, +сокеты и Swarm. + +Подписчик в этом срезе — открытый клиент. Сборщик промпта на шину не подписывается. + +### Важность + +| `severity` | На экране | Часы | Сейв | +| --- | --- | --- | --- | +| `info` | Тост, TTL со стены клиента (ваниль 8 с), клик закрывает | Идут | Нет | +| `warning` / `error` | Модал, висит | Пауза, пока очередь pausing не пуста | Да, до dismiss | + +`pause` на дефе явное: ваниль info — нет, warning/error — да. Play не автозапускается после +последнего закрытия. + +Несколько info — стопка (потолок, старые гаснут). Pausing — очередь модалов, на экране один; +закрыли — следующий. Не больше восьми sticky; девятый не эмитим. + +### Экран и права + +Не колонка. Тост поверх школы. Текст из локали мода (`defName`, как у стула), не с клиента. + +Хозяин закрывает pausing (сокет-намерение, одно поле — id). Гость видит, dismiss pausing и +кнопка картинки — `403` / кадр игнор, как чужой `SetRunning`. Info гость может спрятать у себя; +сервер info не хранит. + +Открытие школы: sticky заново. Info не повторяем. + +После этапа школа целая, даже если стул в промпте молчит. + +## Этап C — вклад в кадр + +«На сцене» при генерации **этого человека**: его узел, `RoomDef`/`TerritoryDef`, fill карты, +надетое и ноша с непустым вкладом, погода **только если место улица** (как тепло). + +Общий блок на `ThingDef`, `RoomDef` и осадках климата (дождь / снег / ясно на улице): + +```jsonc +"prompt": { + "positive": "a classroom blackboard with chalk notes", + "negative": "", + "weight": 1, + "positiveLoras": [], + "negativeLoras": [], + "positiveEmbeddings": [], + "negativeEmbeddings": [] +} +``` + +Пустой блок — вещь молчит. Ваниль заполняет немного: доска, снег, при необходимости форма +(extra к уже существующему слою одежды). Обычный `Chair` без текста. + +Сборка: прежние слои, затем фрагменты сцены (вес по убыванию, при равенстве `defName`). +LoRA сцены — в `loras` / `loraweights` как в фазе 61; embeddings — ``. + +**Бюджет** (числа в `SimulationOptions` / настройке портрета с дефолтом, не магические +константы в сборщике): + +- не больше **6** текстовых фрагментов сцены; +- фрагмент ≤ **120** символов (длиннее на загрузке каталога — ошибка); +- не больше **2** LoRA и **2** embeddings сцены сверх слоёв модели/пресета/кадра. + +«Показать промпт» показывает то же, что уйдёт в Swarm. Нового kind нет. + +После этапа портрет знает комнату; кнопки на событии ещё может не быть. + +## Этап D — действие на событии + +У `EventDef` поле `action`: `none` | `generateImage`. `generateImage` требует `personId` в +`Notice` (кто в кадре). Кнопка на модале/тосте зовёт тот же портретный путь (ваниль — `full`), +со сборкой сцены этапа C. Не рисуем пустую локацию. + +Ваниль: `DayStarted` / `LessonStarted` — info, `none`. `GenerationFailed` — error, пауза, +кнопка повторить того же человека и kind. + +## Где живёт код + +| Что | Куда | +| --- | --- | +| `EventDef`, блок `prompt` на вещах/комнатах/погоде | `HSchool.Content` + JSONC `core` | +| Края дня и звонка как факты | `HSchool.Simulation` (без тостов и Swarm) | +| Доска уведомлений, пауза, исходящие, сейв sticky | `HSchool.Server` (поток школы) | +| Кадры notice / dismiss | `HSchool.Protocol`, `protocol.ts`, `docs/protocol.md` | +| Тост, модал, кнопка | `src/HSchool.Client/src/ui` + `t(...)` | +| Вклад сцены в промпт | `PortraitPromptBuilder` / сервис портретов на работнике | +| Каталог Krea | `swarmui.json` + create-копия | + +Новой игровой библиотеки нет. Инвариант 4 держим: Simulation не ссылается на хост. + +## Советы, которые стоит принять сразу + +- Не возвращать третью колонку «События» из среза 1: оверлей дешевле и не отжимает карту. +- Не клеить уведомление к кадру часов: одно намерение на сообщение; часы 20 Гц, тост — нет. +- Не эмитить «урок начался» по каждому классу: один звонок на школу. +- Не вкладывать в промпт все fill комнаты: только непустой блок и бюджет. +- Не лифтить живые школы «заодно с Krea». +- Не автозапускать часы после dismiss. +- Гость не закрывает pausing — иначе двое смотрящих дерутся за Play. + +## Заведомо не сейчас + +- Журнал школы, фильтры ленты, звук, бейджи на вкладке. +- Kind `scene`, арт узла на дереве карты, ControlNet, поза в UI. +- Условия EventDef как произвольный скрипт (голод < 0.2, мнение < −40). +- Общий EventBus, подписка ИИ на тосты, LLM-текст. +- Лифт `portraitSettings`, Realism v2.0, SDXL в ванильном каталоге. +- Автопортрет каждому ученику утром. + +## Зафиксировано этим разговором + +| Тема | Решение | +| --- | --- | +| Форма | Один срез, не два и не side-work | +| Сцена | Вклад в портрет человека в его узле, не отдельный kind | +| Бюджет | 6 фрагментов, 2 LoRA, 2 embed сцены; пустой def молчит | +| Поля вклада | На Thing / Room / осадках, не отдельный PromptContributorDef | +| Пауза | Сервер; Play игнор, пока есть pausing notice; dismiss не Play | +| Info | Тост, TTL, клик, часы идут, не в сейве | +| Warning/error | Модал до закрытия, в сейве, очередь | +| Права | Pausing dismiss и картинка — хозяин | +| Подписка | Клиент через сокет; сборщик спрашивает комнату | +| Krea | Только Muse + v2.5; child на v2.5; без лифта | +| Картинка с события | Тот же портрет + сцена; нужен personId | +| Ваниль | День, звонок, ошибка генерации | +| Стоп | После B школа с тостами; после C — сцена в промпте без кнопки | +| Протокол | +1 в коммите фазы 64 | +| Ваниль/пак | `core`, не отдельный пак | diff --git a/docs/design/README.md b/docs/design/README.md index f5f8c10..9179871 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -14,4 +14,5 @@ | 8. Сессия и темп | [08-session](08-session/session.md) | | 9. Общение и романтический пак | [09-social](09-social/social.md) | | 10. Ремесло | [10-craft](10-craft/craft.md) | +| 11. События и сцена | [11-events](11-events/events.md) | | Вне очереди | [off-queue](off-queue/) | diff --git a/docs/phases/11-events/63-krea-only.md b/docs/phases/11-events/63-krea-only.md new file mode 100644 index 0000000..cd7ac8e --- /dev/null +++ b/docs/phases/11-events/63-krea-only.md @@ -0,0 +1,40 @@ +# Фаза 63. Каталог Krea 2 + +## Зависимости + +- [Фаза 62](../off-queue/62-portrait-prompt-craft.md) + +## Зачем + +Новая школа больше не предлагает SDXL. Дети рисуются тем же Krea 2, что и остальные, а не +DreamShaper. + +## Задачи + +- [ ] В `swarmui.json` только Muse GGUF и Realism v2.5 INT8 (8 / CFG 1 / euler / simple). + babes, epicrealism, lustify и DreamShaper убрать. `Catalog()` совпадает с файлом +- [ ] Пресеты `default` и `child` — Realism v2.5. Стиль и shotType `child` — проза без + `(word:1.4)` и SD-весов +- [ ] `ageRules` 6–11 по-прежнему ведут на `child`, но модель у child — v2.5 +- [ ] Живые школы не лифтить. Сокет не бампить. PNG не перегенерировать +- [ ] [`protocol.md`](../../protocol.md) — описание каталога без четырёх SDXL, тем же коммитом + (версия сокета не меняется) + +## Тесты, без которых фаза не закрыта + +- [ ] `CreateDefault` содержит ровно два id: Muse и Realism v2.5; нет DreamShaper и babes +- [ ] Resolve взрослого и ребёнка (6–11) берут Realism v2.5 +- [ ] Стиль/негатив child без `(low quality` и без `:1.4)` +- [ ] `GET /api/settings/swarmui` отдаёт только Krea-id; create копирует их в сейв новой школы +- [ ] Нормализация загрузки не переписывает `models` уже лежащей школы с SDXL в каталоге + +## Критерий готовности + +- В диалоге создания школы в пикере только Muse и v2.5 (если Swarm их отдал) +- Новая школа без правок генерирует ребёнка и взрослого с Realism v2.5 +- Уже созданная школа со старым каталогом открывается и рисует по своей копии + +## Стоп + +Не бампить сокет. Не лифтить живые сейвы. Не тащить Realism v2.0. Не писать позу в UI. +Не перегенерировать PNG. Не подключать вклад сцены и тосты. diff --git a/docs/phases/11-events/64-event-defs.md b/docs/phases/11-events/64-event-defs.md new file mode 100644 index 0000000..8ed0b9d --- /dev/null +++ b/docs/phases/11-events/64-event-defs.md @@ -0,0 +1,48 @@ +# Фаза 64. События и info-тост + +## Зависимости + +- [Фаза 16](../04-schedule/16-timetable-in-school.md) (звонок на урок) +- [Фаза 34](../07-inventory/34-wear-home.md) (рабочее утро как край) +- [Фаза 39](../08-session/39-school-owners.md) (гость уже есть; dismiss pausing — фаза 65) + +## Зачем + +Игрок видит, что начался день или урок. Это тост, не колонка и не пауза. + +## Задачи + +- [ ] `EventDef`: id, `severity` (`info` / `warning` / `error`), `pause`, `ttlMs` (0 = не гаснуть), + `trigger` (`dayStart` / `lessonStart` / `generationFailed`), `action` (`none` в этой фазе). + Локаль как у других def. Валидатор каталога +- [ ] Ваниль `DayStarted` и `LessonStarted`: info, без паузы, TTL 8000, `action` none +- [ ] Симуляция на крае рабочего утра и на звонке (смена периода урока, один факт на школу) + отдаёт факт, без UI. Работник сопоставляет def и кладёт info-`Notice` +- [ ] Новый кадр S→C (уведомление: id, defName, severity, pause, ttlMs, optional personId = 0). + Новый кадр C→S dismiss по id. `ProtocolCodec.cs`, `protocol.ts`, [`protocol.md`](../../protocol.md) + в одном коммите. Версия протокола +1 +- [ ] Клиент: тост поверх школы, текст из каталога/`t` по defName, клик шлёт dismiss, TTL + гасит локально. Строки через `t(...)`. Не колонка «События» +- [ ] Info не писать в сейв. Открытие школы info не повторяет. Гость тост видит +- [ ] `generationFailed` в каталоге можно завести, но не эмитить — фаза 67 / 65 + +## Тесты, без которых фаза не закрыта + +- [ ] Каталог грузит `DayStarted` / `LessonStarted`; неизвестный trigger — ошибка загрузки +- [ ] Переход через край рабочего утра даёт один факт `dayStart`; повторный тик в ту же минуту — нет +- [ ] Смена периода на урок даёт один `lessonStart` на школу, не по числу классов +- [ ] Круглый трип и байтовая раскладка кадра уведомления и dismiss — на обеих сторонах +- [ ] Клиентский тест: тост показывает локализованный текст, клик вызывает dismiss, без монтирования колонки +- [ ] Хостовый: открытая школа после утра получает кадр; после Close/Open info-тост не висит из сейва + +## Критерий готовности + +- Запустить часы до начала уроков: тост «начало дня», затем «начало урока», оба сами гаснут +- Клик по тосту убирает его сразу +- Гость той же школы тост видит +- Старый клиент отваливается Hello из‑за версии + +## Стоп + +Не паузить. Не писать sticky в сейв. Не вклад сцены. Не кнопку картинки. Не колонку событий. +Не эмитить по классу. Не бампить протокол отдельно от трёх файлов. diff --git a/docs/phases/11-events/65-warning-pause.md b/docs/phases/11-events/65-warning-pause.md new file mode 100644 index 0000000..6948e43 --- /dev/null +++ b/docs/phases/11-events/65-warning-pause.md @@ -0,0 +1,42 @@ +# Фаза 65. Варнинг и пауза + +## Зависимости + +- [Фаза 64](64-event-defs.md) +- [Фаза 39](../08-session/39-school-owners.md) + +## Зачем + +Ошибка и варнинг останавливают школу, пока хозяин не закроет карточку. F5 не проглатывает их. + +## Задачи + +- [ ] `Notice` с `pause`: работник ставит `running=false`. `SetRunning(true)` игнор, пока есть + хотя бы одно такое уведомление (как нелегальный skip — не fatal) +- [ ] Модал (не тост): висит, очередь если несколько, на экране один. Закрытие — dismiss +- [ ] Sticky в сейве школы; после загрузки и Open — снова кадры. Info по-прежнему не сохранять +- [ ] Dismiss pausing — только хозяин; гость кадр видит, dismiss игнор / HTTP `403` `not-owner` +- [ ] Потолок 8 sticky; девятый не эмитить. После последнего dismiss часы сами не запускать +- [ ] Ванильный `GenerationFailed` (error, pause) можно положить в каталог; эмит с портрета — + фаза 67, здесь достаточно тестового/внутреннего поста на доску +- [ ] [`protocol.md`](../../protocol.md) — поведение паузы и прав, без лишнего бампа версии + (версия уже +1 в 64) + +## Тесты, без которых фаза не закрыта + +- [ ] Пост pausing-notice: `running` становится false; последующий SetRunning(true) оставляет паузу +- [ ] Dismiss последнего pausing: SetRunning(true) снова включает часы; без Play сами не идут +- [ ] Сейв/загрузка: sticky на месте, info нет +- [ ] Гость не снимает паузу dismiss-ом; хозяин снимает +- [ ] Клиентский: модал не гаснет по TTL; очередь показывает следующий после закрытия +- [ ] Хостовый: Open после рестарта работника отдаёт sticky, часы на паузе + +## Критерий готовности + +- Варнинг встаёт на паузу; Play серый/бесполезный, пока модал открыт +- Закрыли — можно нажать Play. F5: модал снова, пауза держится +- Гость модал видит и не закрывает школу + +## Стоп + +Не авто-Play. Не колонку. Не вклад сцены. Не кнопку «создать картинку». Не эмит Swarm в этой фазе. diff --git a/docs/phases/11-events/66-scene-prompt.md b/docs/phases/11-events/66-scene-prompt.md new file mode 100644 index 0000000..0eae801 --- /dev/null +++ b/docs/phases/11-events/66-scene-prompt.md @@ -0,0 +1,48 @@ +# Фаза 66. Сцена в промпте + +## Зависимости + +- [Фаза 62](../off-queue/62-portrait-prompt-craft.md) +- [Фаза 32](../07-inventory/32-weather-warmth.md) (улица vs здание) +- [Фаза 18](../05-ai/18-presence-walking.md) (узел человека) + +Не стоит на 64/65 — можно параллельно с лентой. + +## Зачем + +Почти любой объект может добавить короткий позитив/негатив (и редко LoRA/embed), если он в +кадре этого человека. Стул без текста молчит. Промпт не раздувается. + +## Задачи + +- [ ] Блок `prompt` на `ThingDef`, `RoomDef` (и территории, если двор — тот же блок) и на осадках + климата (ясно / дождь / снег). Пустой = нет вклада. Валидация: строка ≤ 120 символов, + вес конечный ≥ 0 +- [ ] Ваниль: непустой вклад у доски и у снега; обычный `Chair` без блока или с пустым +- [ ] Сборщик (на работнике, не в Simulation): узел человека → комната/двор + fill + worn/held + с блоком + погода только на улице. После слоя одежды. Порядок: вес ↓, затем `defName` +- [ ] Бюджет с дефолтом в опциях/профиле: 6 фрагментов, 2 LoRA сцены, 2 embeddings сцены. + LoRA — параметры Swarm; embeddings — `` как в фазе 61 +- [ ] `GET .../portrait/prompt` включает сцену. Сокет не бампить +- [ ] Живые школы: пустой блок на старых вещах — поведение как вчера + +## Тесты, без которых фаза не закрыта + +- [ ] Каталог: доска даёт positive; Chair не попадает в сборку; строка 121 символ — ошибка загрузки +- [ ] Человек в кабинете с доской: промпт содержит фрагмент доски после одежды; без доски на карте — нет +- [ ] На улице в снег — фрагмент снега; в кабинете в снег — снега нет +- [ ] Больше 6 кандидатов — в тексте только топ-6 по весу; детерминированный тай-брейк по имени +- [ ] LoRA сцены в `loras`, не в тексте; embedding сцены в positive как `` +- [ ] «Показать промпт» / `GET prompt` совпадает со сборкой Generate (без вызова Swarm) + +## Критерий готовности + +- Карточка ученика в классе с доской: «Показать промпт» содержит chalk/blackboard-фразу +- Во дворе в снег — зимняя фраза; в том же снег в кабинете — нет +- Стулья не засоряют промпт +- Старая школа без полей `prompt` генерирует как раньше + +## Стоп + +Не kind `scene`. Не ControlNet. Не бампить сокет. Не кнопку на тосте. Не лифт каталога моделей +(это 63). Не писать вклад в Simulation. diff --git a/docs/phases/11-events/67-notice-image.md b/docs/phases/11-events/67-notice-image.md new file mode 100644 index 0000000..6dcdce0 --- /dev/null +++ b/docs/phases/11-events/67-notice-image.md @@ -0,0 +1,46 @@ +# Фаза 67. Картинка с уведомления + +## Зависимости + +- [Фаза 65](65-warning-pause.md) +- [Фаза 66](66-scene-prompt.md) +- [Фаза 63](63-krea-only.md) (ваниль уже на Krea; не блокер сборки кнопки, но дефолт генерации) + +## Зачем + +Событие может предложить нарисовать человека из факта. Кнопка использует тот же сборщик, что +карточка, включая сцену. + +## Задачи + +- [ ] `EventDef.action`: `none` | `generateImage`. `generateImage` без `personId` на `Notice` — + не рисовать, не 500 +- [ ] Ваниль `GenerationFailed`: error, pause, `generateImage`. Эмит из сервиса портретов при + неуспехе Swarm (тот же personId и kind). Утро и звонок остаются `none` +- [ ] UI: на модале/тосте с action — кнопка; вызов существующего портретного HTTP (или узкого + `POST .../notices/{id}/generate`, если так проще связать права). Kind по умолчанию `full`. + Строки через `t(...)` +- [ ] Хозяин только. Гость кнопку не активирует. Успех не снимает паузу сам — нужен dismiss +- [ ] [`protocol.md`](../../protocol.md) — HTTP действия, тем же коммитом. Сокет не бампить + повторно, если кадр 64 уже несёт action/personId; иначе добавить поля в том же смысле, + что 64, без лишнего +1 если укладка уже есть + +## Тесты, без которых фаза не закрыта + +- [ ] Каталог: `GenerationFailed` с `generateImage`; `DayStarted` без кнопки +- [ ] Неуспех генерации ставит pausing notice с personId; успех — не ставит +- [ ] Хостовый: хозяин с кнопки получает PNG (или тот же outcome, что карточка); гость 403 +- [ ] Промпт этой генерации содержит сцену узла человека (как 66), не пустой коридор без людей +- [ ] Notice без personId и с `generateImage` не дергает Swarm +- [ ] Клиентский: кнопка есть только при action; info-тост дня кнопки не имеет + +## Критерий готовности + +- Сломать/отменить Swarm на портрете: ошибка паузит школу, кнопка перерисовывает того же +- Утро по-прежнему тост без кнопки +- Гость видит модал и не запускает GPU + +## Стоп + +Не автогенерация утром всем. Не арт локации без человека. Не LLM. Не снимать паузу успехом +картинки. Не третью колонку. diff --git a/docs/phases/11-events/README.md b/docs/phases/11-events/README.md new file mode 100644 index 0000000..bed60fc --- /dev/null +++ b/docs/phases/11-events/README.md @@ -0,0 +1,40 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 11. События и сцена + +Дизайн: [`events.md`](../../design/11-events/events.md). + +Мир говорит игроку тостом или варнингом и генератору — коротким вкладом в промпт, если объект +в кадре. Новая школа рисует только Krea 2. Срез можно остановить после этапа B — тосты живые, +даже если стул в промпте ещё молчит. + +**Этап A — только Krea 2.** Каталог без SDXL. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [63. Каталог Krea 2](63-krea-only.md) | ⬜ | Muse + v2.5, child на v2.5, без лифта | + +**Этап B — лента.** Факт, тост, пауза. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [64. События и info-тост](64-event-defs.md) | ⬜ | `EventDef`, утро и звонок, кадр на сокете, тост | +| [65. Варнинг и пауза](65-warning-pause.md) | ⬜ | sticky, Play игнор, сейв, dismiss хозяина | + +63 независима. 65 стоит на 64. + +**Этап C — вклад в кадр.** + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [66. Сцена в промпте](66-scene-prompt.md) | ⬜ | Thing/Room/погода, бюджет, LoRA/embed сцены | + +66 стоит на 62; можно параллельно с 64/65. + +**Этап D — действие на событии.** + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [67. Картинка с уведомления](67-notice-image.md) | ⬜ | `generateImage`, кнопка, тот же сборщик | + +67 стоит на 65 и 66. diff --git a/docs/phases/README.md b/docs/phases/README.md index 743487b..37c7e2c 100644 --- a/docs/phases/README.md +++ b/docs/phases/README.md @@ -21,4 +21,5 @@ | 8. Сессия и темп | 38–40 | [08-session](08-session/README.md) | [session](../design/08-session/session.md) | | 9. Общение и романтический пак | 41–47 | [09-social](09-social/README.md) | [social](../design/09-social/social.md) | | 10. Ремесло | 56–59 | [10-craft](10-craft/README.md) | [craft](../design/10-craft/craft.md) | +| 11. События и сцена | 63–67 | [11-events](11-events/README.md) | [events](../design/11-events/events.md) | | Вне очереди | 48–55 | [off-queue](off-queue/README.md) | [off-queue](../design/off-queue/) |