Перейти к содержанию

«Жизнь канала»: задачи механики для копии 1in8b.life

24 октября 2026. Задание для агента, который ведёт backend «Жизни канала» (internal/chatgames/life/). Владелец, Gorkiy, решил: механику ведёт этот агент, а визуальная часть (сайт, мини-приложение, OBS) остаётся у визуального агента. Цель — максимально близкая копия 1in8b.life по устройству экранов и механикам, со всеми «плюшками», но в формате совместной игры чата на эфире. Код, тексты и картинки оригинала не копируем.

Связанные документы:

  • life-game-design.md: замысел, текущая версия, карта кода, проверки.
  • life-scene-contract.md: публичный контракт для визуальной разработки. Каждое новое публичное поле из этого списка дописывать туда, с типом, смыслом и поведением у старых жизней.
  • Разбор оригинала и план по этапам: документ «Жизнь канала: путь к копии 1in8b.life» (Claude Doc владельца, https://claude.ai/artifact/A5WzJgPTWmMsEhZXVqZkHu).

Как работаем вдвоём

  • Backend: internal/chatgames/life/, src/server/handler_life_*, src/twitch/life_game.go, миграции, этот файл и контракт. Визуальный агент эти файлы не правит.
  • Визуальная часть: ../front-pulse-caster/tg-mini-app/src/components/Life*, src/life*, src/server/assets/life/overlay.html. Backend-агент их не правит; если нужна правка на фронте, опиши её здесь в разделе задачи («Фронт сделает»).
  • Каждая задача — отдельная ветка и PR в master. Сливает Gorkiy. Общение в PR и с владельцем по-русски.
  • Публичный JSON только расширяется необязательными полями (версия состояния 1). Старые жизни продолжаются без пересчёта задним числом; отсутствующее поле означает «нет данных», фронт показывает текстовый fallback.
  • Ничего приватного наружу: seed, flags, seen, followups, будущие исходы, запланированные гости. Любой новый расчёт, который зависит от будущего, отдаётся только после того, как событие произошло.
  • Детерминизм: всё случайное — через roll(seed, ключ, n) с новым уникальным ключом, чтобы повтор жизни и тесты давали один и тот же результат.
  • Когда задача готова, отметь её здесь ([x], номер PR) и допиши поля в контракт. Визуальный агент берёт задачу по этой отметке.

Что уже сделано на фронте (не дублировать)

Этап 1, ветка feature/life-original-board: карта жизни (клетка = год, цвет по years[].stats.happiness, рамка на demography.life_expectancy), блок «Фокус этого года» из focus, circumstances, career, панель «Семья и жизнь» из people[], нумерованные варианты решения с отдельными chance/chance_basis/reason, «Вероятность: X%» при рождении из supported_birth_share, сцены по scene.key, подпись сцены с местом из residence и людьми из scene.cast. Для этого новые поля backend не нужны.

Этап 3: новая механика без тяжёлых данных

Порядок — по пользе для эфира; экономику (3.10) можно брать сразу после 3.1. Оценка: S — до дня, M — 2–4 дня.

3.1 Скорость, «До следующего решения», авторежим (S–M)

  • Backend сделан, PR: #44 (ожидает слияния владельцем).

В оригинале сверху: пауза, скорость 1×/2×/4×/8×/16×, кнопка «До следующего решения» (прокрутить обычные годы до ближайшей развилки) и «Авторежим» (решения принимаются сами).

  • Настройка ведущего speed (1, 2, 4, 8, 16) поверх year_seconds: реальная длина года = year_seconds / speed. Менять можно в любой момент, в том числе в идущей жизни (сейчас темп закрепляется при рождении: эту часть оставить для year_seconds, а множитель сделать оперативным).
  • Действие ведущего skip_to_decision: быстро прожить обычные годы до следующего открытия решения, без пропуска событий летописи (все годы и события записываются как обычно). Ограничение сверху, например не больше 10 лет за раз, и запрет во время открытого решения.
  • Режим auto_decide (флаг настройки): когда включён, решение закрывается сразу воспроизводимым автовыбором, как сейчас при отсутствии голосов, с отметкой в летописи «Авто». В оригинале это «Отныне доверять решения „Авто“».
  • Публично: settings.speed, settings.auto_decide. Состояние «идёт перемотка» можно не отдавать.
  • Twitch-награда и голосование: при auto_decide голосование не открывается и награда не включается.
  • Тесты: длина года с множителем, перемотка останавливается на решении и не теряет события, автовыбор помечен и детерминирован.
  • Фронт сделает: верхнюю панель с кнопками для ведущего в пульте, показ скорости в публичной игре и OBS.

Backend готов к подключению панели: PUT .../settings принимает speed и auto_decide, POST .../skip_to_decision — {"session_id":"UUID"}. Подробности и совместимость описаны в контракте. Включение авторежима учитывает уже принятые голоса/усиления; на паузе закрывает выбор после продолжения. Новые автоматические решения не открывают голосование. Перемотка останавливается после первого такого решения. Для точной полосы на паузе добавлено remaining_milliseconds, у старых жизней fallback — секунды. Базовый year_seconds остаётся закреплённым при рождении.

3.2 «Отложить на время» (S)

  • Сделано, PR: —

В оригинале у решения есть «Отложить на время»: развилка уходит и возвращается позже.

  • Действие ведущего postpone на открытом решении: закрыть без исхода, вернуть сцену в приватную очередь с ближайшим допустимым возрастом (через 1–3 года, в пределах возрастного окна сцены). Если окно закончилось, сцена просто не повторяется.
  • В летописи короткая запись «Решение отложено», без спойлера будущего.
  • Голоса и платные усиления отложенного решения: вернуть так же, как при опоздавшей покупке.
  • Публично: у события kind: "decision" с outcome: "postponed" или отдельный kind — выбрать и описать в контракте.
  • Тесты: сцена возвращается один раз, не возвращается за окном, возврат усилений.

3.3 Родившиеся в ту же секунду (M)

  • Сделано, PR: —

В оригинале рядом с героем живут четыре параллельные жизни, родившиеся в ту же секунду в разных странах по той же статистике рождений. Видно их страну, имя, возраст и короткую строку о том, что с ними сейчас; некоторые уходят раньше.

  • При рождении создать 4 фоновые жизни по тем же весам стран (supported_birth_share), со своим именем, полом, местом и достатком. Отдельный ключ roll, чтобы не менять текущее рождение героя.
  • Каждый год — лёгкая симуляция без выборов: этап жизни, занятие, одно короткое событие раз в несколько лет, переезд, семья. Смерть только в рамках решения 3.x о тяжёлых темах (см. «Решения владельца»); до этого решения фоновые жизни не умирают раньше 60.
  • Публично: peers[]: {key, name, gender, country {key, name, flag}, place, age, alive, status, line}, где status — короткое занятие («учится», «работает в мастерской»), line — последнее событие одной фразой. Портрет фронт рисует сам по gender, skin, hair — добавить эти два поля.
  • В финале: peers[] попадают в биографию одной строкой «Из четырёх, родившихся в ту же секунду…».
  • Тесты: детерминизм, распределение стран по весам на большой выборке, отсутствие приватных полей.
  • Фронт сделает: панель «Родившиеся в ту же секунду» в игре и в комнате памяти.

3.4 Паузы-размышления (M)

  • Сделано, PR: —

В оригинале в 5, 15, 30, 50 и 70 лет жизнь останавливается на короткий вопрос («Что для тебя сейчас важнее всего?»), ответ — одна строка, в финале эти строки возвращаются как «Слова этой жизни».

  • Для эфира: в эти возрасты открывается особое решение kind: "reflection" с 3–4 вариантами-ответами (не свободный текст, чтобы не модерировать чат). Голосование как обычно, но без шансов и денег.
  • Ответ сохраняется в reflections[]: {age, question, answer} и немного влияет на узор жизни (3.5), не на показатели.
  • Публично: decision.kind = "reflection", reflections[] в снимке и в финале.
  • Тесты: открывается ровно в эти возрасты, не ломает очередь обычных развилок, сохраняется в воспоминаниях.
  • Фронт сделает: отдельную тихую карточку вопроса и раздел «Слова этой жизни» в комнате памяти.

3.5 Узор этой жизни (S–M)

  • Сделано, PR: —

В оригинале в мемориале четыре шкалы-пары слов и архетип: «Рядом / Работа», «Корни / Ветер», «Тишина / Волны», «Сегодня / Завтра» и подпись вроде «Тот, кто прокладывает новый путь».

  • Посчитать в backend по уже сохранённым фактам: выбранные ответы (их метки «семья/работа/риск/осторожность» — завести в каталоге теги ответов), переезды (residence), смены работы и перерывы (circumstances), фокусы по годам, ответы пауз-размышлений.
  • Публично в завершённой жизни: pattern {axes: [{key, left, right, value}], archetype, archetype_text}, value от −100 до 100. Слова и архетипы — свои, не из оригинала.
  • Для идущей жизни поле не отдавать (это итог, а не подсказка).
  • Тесты: стабильность расчёта, каждая шкала реагирует на свои факты, старые жизни без тегов получают нейтральные значения.
  • Фронт сделает: раздел «Узор этой жизни» в комнате памяти и строку на OBS-финале.

3.6 Эпилог близких, «Последние мгновения», «Сцена после титров» (M)

  • Сделано, PR: —

В оригинале после смерти: что стало с близкими через годы, последние мгновения героя и короткая сцена после титров.

  • В финале сгенерировать epilogue[]: {person_key, name, role, text, scene} по живым на момент конца близким — 1–2 предложения о том, как сложилась их жизнь, на основе их path, goal, interest. Без выдуманной ранней истории, только вперёд от момента смерти.
  • last_moments {text, scene, people}: где и с кем, по residence, близким и последним событиям.
  • post_credits {text, scene}: одна тёплая деталь из keepsakes или самого частого фокуса.
  • scene в формате контракта (key, cast, props, mood), чтобы фронт нарисовал пиксельную сцену.
  • Тесты: эпилог только для живых, детерминизм, ссылки на настоящие event_ids.

3.7 Справки «Данные» у событий и решений (M)

  • Сделано, PR: —

В оригинале под событием летописи и в окне решения есть строка «Данные» с реальным фактом и источником (ВОЗ, ЮНИСЕФ и т. п.).

  • Завести каталог фактов data/facts.json: {key, country | "world", text, value, unit, year, source, url}. Только проверенные числа из открытых источников, с годом; никаких сгенерированных цифр. Покрытие — шесть стран игры.
  • Связать сцены и бытовые эпизоды с фактами по тегу (школа → доля охвата школой, работа → занятость, рождение → детская смертность до 5 лет и т. п.).
  • Публично: events[].data и decision.data: {text, source, url, year}. Необязательное поле.
  • Тесты: каждый факт имеет источник и год, у отсутствующего факта поле не отдаётся.
  • Фронт сделает: строку «Данные» с раскрытием под событием и в решении.

3.8 Регистрация рождения целиком (S)

  • Сделано, PR: —

В оригинале карточка «Регистрация рождения» с номером документа, связным абзацем, доходом семьи по квинтилям, жильём, возрастом и работой родителей, ожидаемой продолжительностью и смертностью до 5 лет.

  • Публично в character: income_quintile (1–5, из уже выбираемого достатка: скромный → 1–2, средний → 3, обеспеченный → 4–5, детерминированно) и housing (короткая строка). Религию не выдумывать.
  • В country.demography добавить under5_mortality_per_1000 (WDI SH.DYN.MORT) и gdp_per_capita_usd (WDI NY.GDP.PCAP.CD) тем же скриптом scripts/update-life-demography.py, с годом и источником.
  • Тесты: полнота набора, новые индикаторы не нули.
  • Фронт сделает: карточку регистрации, точечную карту мира и «Начать эту жизнь» для ведущего.

3.9 Счётчики на главной (S–M)

  • Сделано, PR: —

В оригинале на главной: «пока вы здесь, родилось N человек», «сейчас живёт», «тема дня».

  • Публичный endpoint без авторизации: число активных жизней всех каналов, число завершённых жизней, страны рождения последних 100. Без имён каналов, если они не публичные.
  • «Родилось пока вы здесь» фронт считает сам по мировой рождаемости, backend не нужен.

3.10 Экономика: карьерный рост, своё дело, жильё и аренда, инфляция, акции (M–L)

  • Backend сделан, PR: #46 (ожидает слияния владельцем).

Gorkiy отдельно назвал это «очень сильными механиками». Сейчас есть только деньги (finance), покупка «Собственное жильё» за 45 000 и career {track, level 0–3} с треком business. Нужно превратить это в настоящую экономику жизни, где решения чата видны через годы. Числа — игровая модель с понятной логикой, а где есть открытая статистика (инфляция, доходность), брать её с источником.

Карьерный рост на работе.

  • Уровни внутри трека шире 0–3: должность с названием (стажёр → специалист → ведущий → руководитель и т. п., свои слова для каждого трека), зарплата растёт со ступенью и опытом.
  • Развилки повышения: «взять больше ответственности» с шансом по знаниям, опыту и нагрузке (circumstances.load), переход в другую компанию, учёба ради роста. Неудача — outcome: setback, без потери всего.
  • Публично: career.title, career.salary (годовой доход от работы), career.promotions (число повышений).

Своё дело.

  • Развилка «открыть своё дело» с 20 лет: стартовые вложения из cash или в долг, ежегодный результат зависит от трека, знаний, нагрузки и случайности; бизнес может расти, держаться или закрыться с долгом.
  • Публично: business {name, kind, since_age, value, yearly_profit, employees, status}; status: growing, stable, struggling, closed.
  • События: первый сотрудник, тяжёлый год, предложение продать дело.

Жильё и аренда.

  • Вместо одной покупки — список недвижимости: своё жильё и квартиры для сдачи. Покупка за наличные или в ипотеку (debt с ежегодным платежом и процентом).
  • Сдача в аренду: ежегодный доход, простой без арендатора, ремонт. Цена жилья меняется с годами (рост с инфляцией плюс случайные колебания).
  • Продажа — решение чата, деньги идут в cash.
  • Публично: assets.properties[] {key, kind, bought_age, price, value, rented, rent_income, mortgage_left}.

Инфляция.

  • Годовая инфляция по стране проживания (WDI FP.CPI.TOTL.ZG, тем же скриптом данных, с годом и источником), для будущих лет — сглаженное значение. Растут цены покупок, расходы и аренда; наличные без вложений теряют покупательную силу.
  • Публично: finance.inflation (процент года), finance.real_cash (в ценах года рождения) — чтобы фронт показал «в ценах года рождения».

Вклады и акции.

  • Развилки вложить часть денег: вклад (низкий надёжный процент), облигации, акции (высокая доходность в среднем и большой разброс), рискованная «горячая» компания, которая может сгореть до нуля. Кризисный год бьёт по всем акциям сразу.
  • Детерминированно через roll, без подсказок будущего: шанс показывается как и сейчас (chance, chance_basis), итог видно только в следующем году.
  • Публично: assets.investments[] {key, kind, invested, value, since_age, status}; status: active, sold, burned. assets.net_worth = наличные + имущество + вложения − долги, по годам в years[].stats.net_worth для графика.

Общее.

  • Покупки ведущего за деньги остаются, но жильё переходит в assets.properties.
  • В финале: строки для мемориала «Состояние в конце жизни», «Сдавал(а) N квартир», «Своё дело 12 лет» — в biography или отдельным summary.
  • Тесты: детерминизм, инфляция по стране, ипотека гасится, сгоревшая акция не возвращается, нет отрицательных value, старые жизни без assets продолжаются.
  • Фронт сделает: панель «Деньги и имущество» с капиталом и графиком, карточки домов и вложений, сцены покупки, сдачи, кризиса и закрытия бизнеса.

Backend подготовлен отдельным изменением: шесть ступеней карьеры, бизнес с годовым результатом, имущество/ипотека/аренда, вложения и исторические годовые точки капитала. POST .../games/life/economy открывает финансовую тему обычным голосованием; поддержаны выбор конкретного объекта и лимит повторного обсуждения в году. Контракт 3.10 содержит поля, примеры и точные тела запросов для фронта. Инфляция — среднее WDI 2019–2023; акции/облигации — исторические сценарии США 2019–2023 (NYU Stern), а не обещание будущей доходности или национальные ставки. Остальные коэффициенты явно игровые. Старые жизни остаются на прежней модели до явного финансового действия; прошлое имущество, цены, зарплаты и капитал не дорисовываются. Новый SQL не нужен, состояние версии 1 расширено необязательными полями.

Этап 4: реальная статистика и сообщество

Решения владельца по этим пунктам приняты (см. ниже); это большие задачи, их лучше брать после этапа 3.

  • Больше стран (L): расширить набор WDI и профили стран (места, имена, занятия). Предложить владельцу список следующих 10–20 стран и минимальный профиль, который не требует авторского текста на каждую.
  • Таблицы смертности, болезни и кривая выживаемости (L): половозрастные таблицы ООН WPP, вероятность дожить до каждого возраста, причины по данным ВОЗ; публично survival[] для кривой. Решено: как в оригинале, по статистике (см. ниже). Можно начинать.
  • Доход по ППС и Джини, суммы «по ценам России» (L): отдельная модель дохода, не умножение зарплатного коэффициента на ВВП. Сохранить игровые единицы как есть и добавить пересчёт для показа.
  • Свечи и книга гостей (M + модерация). Решено владельцем, можно начинать:
  • Писать и зажигать свечу можно только после входа через Twitch. Свеча — одна на зрителя на жизнь, без текста.
  • Запись в книге гостей сначала уходит на модерацию ведущему и до одобрения видна только автору и ведущему.
  • Доверие: когда у автора на этом канале уже есть несколько одобренных записей (порог в настройках, по умолчанию 3), новые публикуются сразу, без премодерации. Ведущий всё равно может позже скрыть такую запись, а при скрытии доверие автора сбрасывается.
  • Кнопка «Пожаловаться» у каждой записи для вошедших зрителей; запись с жалобами (порог в настройках) скрывается до решения ведущего. Очередь жалоб и модерации в пульте ведущего.
  • Лимиты против спама: длина записи, не чаще одной записи на жизнь в N минут, бан автора на канале.
  • Публично только отображаемое имя Twitch и текст, без ID и других личных данных; в OBS записи по умолчанию не выводить.
  • Хранение: новая Goose-миграция (записи, свечи, жалобы, доверие автора), публичный API выдаёт только одобренные записи.
  • Фронт сделает: свечу с «Кто-то зажёг здесь свечу», книгу гостей в комнате памяти, кнопку жалобы и очередь модерации в пульте.

Решения владельца

Приняты 24.10.2026:

  • Механику ведёт backend-агент, визуальную часть — визуальный агент, задачи передаются через этот файл.
  • Тяжёлые события (болезни, ранняя смерть) — на основе статистики, как в оригинале. Подавать спокойно, без графических подробностей; фоновые жизни из 3.3 тоже умирают по статистике.
  • Экономика (3.10) — в приоритете: Gorkiy считает её очень сильной механикой.

  • Свечи и книга гостей: после входа через Twitch, премодерация, доверие после нескольких одобренных записей, поздняя модерация и кнопка жалобы (подробности в этапе 4).

Ещё нужно: порядок задач внутри этапа 3, если нужно не так, как здесь.

Проверки перед PR

  • go test ./internal/chatgames/life, focused TestLife в src/twitch и src/server.
  • golangci-lint --concurrency 2 по изменённым пакетам.
  • Новая Goose-миграция, если меняется схема; sqlc generate.
  • Примеры в examples/life-scene-fixtures.json обновить, если появились новые поля, чтобы визуальный агент мог рисовать без production-данных.
  • GitHub Actions сейчас может не запускаться из-за биллинга: прогнать проверки локально и написать результаты в PR.