О проекте
Команда: Hahatun
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Городской симулятор: игрок выбирает 5 из 14 мер в бюджете 100 у.е., детерминированный движок считает Astana Quality of Life Score по районам, а консилиум AI-агентов объясняет результат и выносит резолюцию. Есть лидерборд, городские шоки, песочница с собственным датасетом и озвучка резолюции.
Как устроено
Валидатор проверяет 8 правил кейса, движок считает Score с лагами, синергиями, весами и штрафами, затем генерируются пронумерованные факты F1…Fn. Оптимизатор полным перебором всех 694 395 валидных наборов находит глобальный оптимум, перцентиль и улучшения заменой одной меры. Консилиум: 6 LLM-экспертов параллельно → синтезатор → ревизоры C1–C6 (C1–C2 кодом сверяют числа) до 2 кругов → арбитр; прогресс идёт по SSE. Числа LLM не считает, это проверяется кодом.
Ключевые решения
- мультиагентный консилиум
- полный перебор 694k наборов
- grounding через ID фактов
- проверка чисел кодом
- structured outputs (zod strict)
- SSE-стриминг
- OpenAI TTS
Метрики
базовый Score 52.56, пример организаторов 56.54 воспроизведён золотым тестом; глобальный оптимум 57.24; перебор 694 395 наборов за ~0.4 с
Особенности
Принцип «код считает, LLM говорит»: всё количественное детерминировано, LLM ссылается на ID фактов, а ревизоры кодом сверяют числа в тексте. Полный перебор пространства решений даёт честный разрыв с оптимумом. Таймлайн 8 кварталов с лагами мер, неожиданные события с пересчётом, песочница со своим датасетом, 3D-Байтерек на лендинге.
Стек
TypeScript · Next.js · React · Tailwind CSS · shadcn/ui · Recharts · three.js · Vercel AI SDK · Zod · Vitest · Playwright · Docker
Модели
gpt-5-mini · gpt-5 · gpt-5.4-mini · gpt-5.5 · gpt-4o-mini-tts
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-4fbaf6fd-hahatun.
README
README.mdАким на 5 часов
AI-симулятор управления городом для кейса HackAlem AI: бюджет, пять решений по районам Астаны, Astana Quality of Life Score и консилиум AI-агентов, который объясняет результат и выносит резолюцию.
Как играть за минуту. Вы аким. У вас 100 условных единиц и 14 мер по пяти направлениям. Выбираете ровно пять, движок сразу показывает Score, консилиум объясняет и выносит резолюцию.
Словарь. Score — Astana Quality of Life Score по формуле кейса, 0–100. Консилиум — цепочка AI-агентов: шесть экспертов по направлениям, синтезатор заключения, ревизоры и арбитр. Ревизоры — шесть проверок заключения (C1–C6), первые две делает код: числа в тексте совпадают с расчётом, рекомендация валидна. Арбитр — итоговая резолюция: утвердить, утвердить с условиями, вернуть. Фолбэк — режим без ключа OpenAI, тексты из заготовки, числа те же.
1. Краткое описание
Проблема. При развитии города нужно учитывать сразу пять направлений: транспорт, озеленение, социальную инфраструктуру, безопасность и качество городских сервисов. Ресурсы ограничены, каждое решение сдвигает десятки показателей, и последствия набора решений трудно оценить в голове.
Для кого. Городской управленец, аналитик или пользователь симулятора (формулировка кейса), а также студенты и участники деловых игр, которым нужно быстро прогнать сценарий, увидеть его цену и услышать аргументы «за» и «против».
Принцип. Код считает, LLM говорит. Валидация, Score, дельты по районам, поиск улучшений и исход резолюции считаются детерминированно. Языковая модель получает пронумерованные факты и объясняет их, спорит и пишет резолюцию. Ни одно число не приходит из модели, и это проверяется кодом. Так требует сам кейс, раздел «Роль ИИ» датасета: «LLM получает результат расчёта (дельты по районам и показателям, вклад каждой меры) и объясняет его, сравнивает наборы, советует. Числа он не считает и не придумывает».
Соответствие кейсу
Must have из задания:
| Требование кейса | Как закрыто |
|---|---|
| Единый виртуальный бюджет для всех пользователей | 100 у.е. и одинаковые исходные данные для всех команд, константы в src/lib/types.ts и src/lib/data/ |
| Решения по 5 направлениям: транспорт, озеленение, социальная инфраструктура, безопасность, городской сервис | каталог из 14 мер датасета, сгруппированный по пяти направлениям; ровно 5 решений, не больше 2 на направление |
| Автоматический контроль превышения бюджета | валидатор блокирует отправку набора дороже 100 у.е., полоса бюджета краснеет и показывает перерасход |
| AI-анализ принятых решений | консилиум: 6 экспертов, синтезатор, ревизоры, арбитр (OpenAI API) |
| Расчёт итогового Astana Quality of Life Score | детерминированный движок по формуле датасета, живой пересчёт в кабинете |
| Объяснение сильных сторон, рисков и возможных последствий | черновик заключения с разделами «сильные стороны / риски / последствия» и резолюция арбитра |
Опционально:
| Опция кейса | Статус |
|---|---|
| Сравнение результатов нескольких команд | есть: лидерборд /leaderboard по сохранённым прогонам |
| Визуализация изменений показателей районов | есть: схема районов Астаны с цветом по оценке района, радар «до / после», таблица показателей со стрелками |
| AI-рекомендации по улучшению сценария | есть: улучшения считает оптимизатор, арбитр оформляет их в поручения, блок «Что сделать дальше» с кнопкой «Применить» |
| Неожиданные городские события | есть: после вердикта событие бьёт по показателю района, Score пересчитывается тем же набором, предлагается лучшая замена и кнопка «Перераспределить бюджет» |
| Генерация краткой презентации решения | есть: «Скачать краткую презентацию (.md)» на странице вердикта |
Критерии проверки из кейса и где их увидеть:
- Одинаковый бюджет и исходные данные: брифинг
/показывает базу 52.56 и бюджет 100 для любой команды. - Бюджет нельзя превысить: в
/playнабор дороже 100 у.е. не отправляется, причина названа (п. 7 ниже). - Решения влияют на показатели: вердикт показывает изменения каждого показателя по районам и вклад каждой меры.
- Понятное объяснение и компромиссы: зал заседаний (эксперты, споры, резолюция с обоснованием).
- Другой набор даёт другой Score: замена одной меры в кабинете сразу меняет Score (п. 7 ниже).
2. Что реализовано
- Единый бюджет 100 и синтетический датасет организаторов: 5 районов, 10 показателей, 14 мер с ценой, лагом и эффектами, 3 синергии, 3 несовместимости.
- Валидатор восьми правил кейса, возвращает все нарушения списком: ровно 5 решений, бюджет, повторы, район для локальных мер, не более 2 мер на направление, конфликты.
- Движок Score по формуле датасета: лаг, синергии, обрезка 0..100, веса, штраф за критические значения. Золотой тест воспроизводит базу 52.56 и пример организаторов 56.54.
- Факты с ID (
F1…Fn): каждое изменение показателя, вклад каждой меры, слабейший район, критические значения, остаток бюджета. - Оптимизатор: полный перебор всех 694 395 валидных наборов за ~0.4 с с кэшем на диске. Даёт глобальный оптимум (57.24), перцентиль набора среди всех валидных и до трёх улучшений заменой одной меры.
- Консилиум из восьми стадий: 6 экспертов (транспорт, экология, соцсфера, безопасность, сервисы, финансы) параллельно → синтезатор → ревизоры (6 условий, порог 5 из 6, до 2 кругов пересмотра) → арбитр с резолюцией «утвердить / утвердить с условиями / вернуть».
- Стриминг прогресса по SSE, сохранение прогона в JSON, страница результата открывается по ссылке.
- Режим без ключа OpenAI: движок и оптимизатор работают, консилиум отвечает детерминированным фолбэком с пометкой.
- Внезапное городское событие после вердикта: шок по показателю района (авария теплосетей, смог, паводок и др.), пересчёт Score и предложение перераспределить одну меру.
- Экспорт краткой презентации решения команды в markdown одной кнопкой.
- Таймлайн 8 кварталов на вердикте: ползунок показывает, как растут показатели и Score по мере срабатывания лагов (линия ЛРТ с лагом 4 ничего не даёт до пятого квартала). Считается той же моделью, на 8-м квартале совпадает с итогом.
- Разрыв с оптимумом: под Score видно лучший возможный набор, на сколько он выше и какими заменами отличается, кнопка «Открыть оптимум».
- Песочница: импорт своего датасета в формате кейса на странице
/sandbox. Шаблонpublic/dataset-hackalem.jsonэто данные кейса; можно поменять показатели и доли районов, цены, лаги и эффекты мер, веса, синергии и конфликты. Файл проверяется схемой (значения 0–100, сумма весов и долей населения 1, все 5 районов и 14 мер). Весь конвейер, включая полный перебор и консилиум, работает на загруженных данных. Прогоны песочницы помечены и не попадают в лидерборд, поэтому соревнование остаётся на одинаковых данных для всех команд. - Озвучка резолюции: кнопка «Озвучить резолюцию» читает постановление арбитра голосом (OpenAI TTS,
gpt-4o-mini-tts), аудио кэшируется на прогон. - Интерфейс: брифинг со схемой районов, кабинет решений с живым Score, экран заседания со стадиями по мере готовности, вердикт с залом заседаний, лидерборд команд.
3. Как работает решение
- Брифинг (
/): 3D-модель Байтерека, схема районов Астаны с цветом по оценке района, пять карточек районов, базовый Score 52.56, два критических провала Нуры (школы 38, поликлиники 35). Команда вводит название. - Кабинет решений (
/play): каталог мер по пяти направлениям, выбор района, бюджетная полоса, лимиты по направлениям, живой Score прямо в браузере тем же движком. Невалидный набор блокирует отправку и называет причины. - Заседание (
/result/live): восемь стадий пайплайна отмечаются построчно по мере событий SSE, карточки экспертов, ревизия и резолюция появляются по ходу. По событиюdoneоткрывается вердикт. - Вердикт (
/result/[runId]): Score и дельта, перцентиль, схема районов и радар района до и после, таблица показателей, блок «Как считалось» с формулой, консилиум: эксперты, «Ревизия: N из 6 условий», «Черновики заключения» (отклонённые фразы подсвечены), резолюция и «Споры и поручения арбитра». Блок «Что сделать дальше» с кнопкой «Применить» подставляет улучшение в кабинет для нового прогона. Ниже карточка внезапного события и ссылка на презентацию. - Лидерборд (
/leaderboard): все сохранённые прогоны на данных кейса, выше тот, чей набор дал больший Score. - Песочница (
/sandbox): скачать шаблон, изменить данные, загрузить файл, посмотреть превью районов и базового Score, играть на своих данных. Плашка «Песочница» видна в кабинете и на вердикте.
| Брифинг | Кабинет решений | Вердикт |
|---|---|---|
![]() | ![]() | ![]() |
Полная страница вердикта с залом заседаний: docs/screenshots/verdict-full.png.
Сценарий → Валидатор → Движок + Факты → Оптимизатор
→ 6 экспертов (LLM, параллельно) → Синтезатор (LLM)
→ Ревизоры: C1, C2 код · C3–C6 LLM ──┐ провал, круг ≤ 2
↑___________________________│
→ Таблица исходов (код) → Арбитр (LLM) → Запись прогона → SSE done
4. Технологии
- Next.js 16.3 (App Router, route handlers, SSE), React 19.2, TypeScript 5
- Tailwind 4 + shadcn/ui, Recharts 3, lucide-react, three.js (3D-Байтерек на главной, грузится лениво только на
/) - Vercel AI SDK 7 +
@ai-sdk/openai4, structured outputs через zod 4 (strict JSON schema) - OpenAI API через
OPENAI_EXPERT_MODELиOPENAI_JUDGE_MODEL: по умолчаниюgpt-5-miniиgpt-5; прогоны и числа в этом README сделаны наgpt-5.4-mini(эксперты) иgpt-5.5(синтезатор, ревизоры, арбитр) - Vitest 5, Docker (standalone-сборка Next)
5. Архитектура
src/lib/data/ датасет как константы (районы, меры, веса, синергии, конфликты)
src/lib/engine/ validator, engine (Score), facts (факты с ID), optimizer — чистые функции
src/lib/consilium/ llm (единая точка вызовов), experts, synthesizer, reviewers, arbiter, pipeline
src/lib/store/ прогоны в JSON: data/runs/<id>.json
src/app/api/run POST → поток событий ConsiliumEvent (SSE)
src/app/api/runs GET список, GET по id, GET /event (событие), GET /pitch (презентация .md), GET /voice (озвучка .mp3)
src/app/ брифинг, кабинет, заседание, вердикт, лидерборд
src/components/ экраны UI: briefing, play, live, result, consilium, map, leaderboard, landing (3D-Байтерек), shell, ui
src/lib/ui/ форматтеры, разбор SSE, сборка прогона из событий
src/lib/types.ts контракт между UI и бэком
fixtures/ полный образец прогона для разработки UI
| Что | Кто делает | Почему |
|---|---|---|
| Валидация, Score, дельты, вклад мер | код | воспроизводимость, критерии 2, 3, 5 кейса |
| Поиск улучшений, глобальный оптимум, перцентиль | код, полный перебор | 694 395 наборов за 0.4 с, детерминированно и воспроизводимо |
| Мнения экспертов, черновик, споры, резолюция | LLM | нужен язык, а не арифметика |
| Проверка чисел в тексте (C1), валидность рекомендации (C2) | код | LLM не проверяет сам себя там, где может код |
| Полнота и честность текста (C3–C6) | LLM-ревизор | условия, а не «оценка уверенности» |
| Исход резолюции | код, таблица условий | один ответ на вопрос «почему утвердили» |
6. Установка и запуск
git clone https://github.com/BAITC-Hacks/hack-4fbaf6fd-hahatun.git
cd hack-4fbaf6fd-hahatun
yarn install
cp .env.example .env # вписать OPENAI_API_KEY, без него работает фолбэк
yarn dev # http://localhost:3000
Docker:
cp .env.example .env
docker compose up --build # http://localhost:3000, прогоны в volume runs
Проверки: yarn test (движок, валидатор, ревизоры, пайплайн), yarn typecheck, yarn lint. Приёмка в браузере: npx playwright install chromium один раз, затем yarn test:e2e при запущенном yarn dev (брифинг → кабинет → консилиум → вердикт → лидерборд → презентация → событие, 5 тестов, около минуты с ключом).
7. Как проверить решение
Через интерфейс
- Открыть
/play, собрать пример организаторов: M7 Нура, M8 Нура, M10 Нура, M12, M5 Сарыарка. Стоимость 95, живой Score показывает 56.54 (в документе кейса ≈ 56.5). Тот же набор открывается ссылкой/play?s=M7.nura,M8.nura,M10.nura,M12,M5.saryarka. - Заменить M5 на M3 (ЛРТ) в Нуре: Score меняется на 57.21. Убрать одну меру: кнопка «На консилиум» блокируется, панель набора показывает «4 из 5» и причину «Нужно ровно 5 решений, сейчас 4». Выбрать вместе M1 и M3: причина «„Выделенные полосы для автобусов“ и „Линия ЛРТ / расширение“ несовместимы» (в кабинете меры названы по имени, коды остаются в API и презентации).
- Превысить бюджет: в примере заменить M12 на M3 в Нуре (
/play?s=M7.nura,M8.nura,M10.nura,M3.nura,M5.saryarka). Стоимость 111, полоса бюджета красная с подписью «перерасход на 11 у.е.», причина «Бюджет 100 превышен: стоимость набора 111», отправка заблокирована. - Нажать «На консилиум». На экране заседания стадии отмечаются по мере готовности, затем на вердикте появляются шесть экспертов, черновик, ревизия и резолюция. С ключом прогон занимает 30–60 секунд и стоит около 4 центов; без ключа около секунды, тексты помечены как фолбэк.
Через API
Сервер на 3000:
curl -N -X POST http://localhost:3000/api/run -H 'Content-Type: application/json' \
-d '{"teamName":"Демо","scenario":{"decisions":[{"measureId":"M7","districtId":"nura"},{"measureId":"M8","districtId":"nura"},{"measureId":"M10","districtId":"nura"},{"measureId":"M12"},{"measureId":"M5","districtId":"saryarka"}]}}'
Ожидаемый поток: stage validate/engine/optimize, событие engine со score: 56.54 и 32 фактами, optimizer с улучшением «M5 Сарыарка → M3 Линия ЛРТ, Нура, 57.21», шесть событий expert, draft, review (с ключом обычно 6 из 6), resolution с исходом approve и поручением, done с runId. Затем GET /api/runs/<runId> возвращает сохранённый прогон, GET /api/runs/<runId>/event — внезапное событие с пересчётом, GET /api/runs/<runId>/pitch — презентацию в markdown. Реальный прогон с LLM сохранён как образец в fixtures/live-run.json.
Три сида с ожидаемыми результатами
Движок детерминирован, числа совпадут; тексты LLM и ревизия могут отличаться от прогона к прогону.
| Набор | Стоимость | Score | Крит. значений | Исход арбитра |
|---|---|---|---|---|
| M7 Нура, M8 Нура, M10 Нура, M12, M5 Сарыарка (пример кейса) | 95 | 56.54 | 0 | утвердить |
| M2, M3 Нура, M8 Нура, M9 Нура, M14 (глобальный оптимум) | 98 | 57.24 | 0 | утвердить, перцентиль 100% |
| M9 Нура, M11 Есиль, M10 Нура, M12, M4 Сарыарка (самый дешёвый) | 61 | 55.17 | 1 (Нура S2) | утвердить с условиями, поручение заменить меру на M8 Нура |
Тесты: yarn test — 141 тест, среди них золотой тест формулы (база 52.56, пример 56.54, синергия M10+M12), валидатор по каждому правилу, проверка чисел ревизора C1, цикл пересмотра с cap 2, хранилище с защитой от path traversal.
8. Данные и интеграции
- Синтетический датасет организаторов (
docs/source/dataset.md), перенесён в код без изменений. Персональных данных нет. - OpenAI API через Vercel AI SDK. Ключ только в
.env, в репозитории его нет. - Внешних БД и сервисов нет, прогоны хранятся файлами.
- Защита командного ключа от расхода: не более 3 одновременных прогонов и пауза 5 секунд между запусками с одного адреса (
429), тело запроса ограничено, порт в Docker слушает только localhost. Секреты только в.env,yarn auditбез уязвимостей.
9. Ограничения
- Кэш перебора
data/optimum.json(5.5 МБ) создаётся при первом прогоне, это добавляет около секунды к первому запросу. - Ревизия ограничена двумя кругами, после них результат отдаётся с бейджем «проверено N из 6».
- Один город, один датасет, без авторизации и мультиплеера. Лидерборд показывает прогоны, сохранённые на этом сервере.
- Мобильная вёрстка не приоритет, целевой экран от 1024px.
10. Потенциал развития
- Реальные данные районов и открытые источники акимата вместо синтетики.
- Замена LLM-ревизоров быстрым классификатором (например, Laya на ModernBERT) после накопления размеченных вердиктов.
- Импорт реальных данных акимата через песочницу с расширением схемы: новые меры и районы, а не только изменение значений.
- Каталог событий шире шести штук и события в середине горизонта, а не после вердикта; экспорт презентации в PDF/PPTX.
- Вход по ЭЦП через NCA Node для внедрения в акимате.
11. Развёрнутая версия
Пока нет, запуск локально или через Docker.
Команда
- Амир Бауржанов — движок Score и полный перебор, консилиум (эксперты, синтезатор, ревизоры, арбитр), SSE API и хранилище, события и экспорт презентации, README, ревью и приёмка.
- Мирас Есеналы — дизайн-система и оформление в стиле бланка акимата (
DESIGN.md), 3D-Байтерек на three.js, брифинг со схемой районов, кабинет решений с живым Score, экран заседания и вердикт с консилиумом, лидерборд, клиент SSE и тесты UI-логики.
Авторы коммитов
История коммитов
- 2026-09-23 17:02merge: dataset-aware measure names and tie-aware leaderboard ranks (539aa24) into mainmiras
- 2026-09-23 16:56fix(ui): measure names follow the run's dataset; tie-aware leaderboard ranksmiras
- 2026-09-23 16:48docs: README markdown lint fixes (headings, fence languages), test counta.baurzhanov1
- 2026-09-23 16:45fix(api): validate the dataset before rate limits tooa.baurzhanov1
- 2026-09-23 16:44merge: validate body before rate limits (5ac4f1b) into maina.baurzhanov1
- 2026-09-23 16:43merge: dataset import sandbox into maina.baurzhanov1
- 2026-09-23 16:43fix: sandbox dataset hardening — fixed scope/direction, safe titles, small cache, leaderboard fallbacka.baurzhanov1
- 2026-09-23 16:39fix(api): validate the run body before rate limitsmiras
Все коммиты (86)
- 2026-09-23 16:38docs: README sandbox import sectiona.baurzhanov1
- 2026-09-23 16:37feat: dataset import sandbox — core threading, UI, leaderboard filter, e2ea.baurzhanov1
- 2026-09-23 16:37merge: main into feat/importa.baurzhanov1
- 2026-09-23 16:33merge: UI polish for timeline and voice (a06571e) into maina.baurzhanov1
- 2026-09-23 16:29feat: sandbox dataset contract, schema and case dataset template (import)a.baurzhanov1
- 2026-09-23 16:28fix(ui): polish the wow wave to DESIGN.mdmiras
- 2026-09-23 16:21docs: backlog statuses after wow wavea.baurzhanov1
- 2026-09-23 16:20merge: wow wave (timeline, optimum gap, voice) into maina.baurzhanov1
- 2026-09-23 16:20fix: voice text hard-cut fallback, hide provider error detailsa.baurzhanov1
- 2026-09-23 16:19docs: README and env for timeline, optimum gap, voicea.baurzhanov1
- 2026-09-23 16:18feat: quarter timeline slider on the verdict (W2)a.baurzhanov1
- 2026-09-23 16:18merge: main (round 5) into feat/wowa.baurzhanov1
- 2026-09-23 16:16docs: overspend label wordinga.baurzhanov1
- 2026-09-23 16:16merge: UI review fixes (round 5) into maina.baurzhanov1
- 2026-09-23 16:13feat: voice of the resolution via OpenAI TTS with per-run cache (W3)a.baurzhanov1
- 2026-09-23 16:12feat(ui): plain header, review fixes from Амирmiras
- 2026-09-23 16:12feat: per-quarter timeline helper for the verdict (W1)a.baurzhanov1
- 2026-09-23 16:10docs: wow wave plan (timeline, voice, optimum gap) in feat/wowa.baurzhanov1
- 2026-09-23 16:07docs: screenshots refreshed for the round-4 UIa.baurzhanov1
- 2026-09-23 16:06docs: README aligned with round-4 UI wording, dedupe intro, trim team sectiona.baurzhanov1
- 2026-09-23 16:01docs: README jury audit fixes - test count, optimum cost, model defaults, glossarya.baurzhanov1
- 2026-09-23 16:00merge: UI round 4 + WebGL fallback into maina.baurzhanov1
- 2026-09-23 15:59fix(ui): survive WebGL renderer failure on the landing, show decisions countera.baurzhanov1
- 2026-09-23 15:55merge: UI round 4 (Baiterek v2, lighter verdict, human measure names) into maina.baurzhanov1
- 2026-09-23 15:52feat(ui): no measure codes for players - short names instead of M1..M14miras
- 2026-09-23 15:49refactor(ui): lighter cabinet and verdict - one job, one next stepmiras
- 2026-09-23 15:36Merge remote-tracking branch 'origin/main' into feat/uimiras
- 2026-09-23 15:33merge: UI round 3 + security and review fixes into maina.baurzhanov1
- 2026-09-23 15:32fix: run throttling and body cap, localhost port, partial LLM status in UI and pitch, e2e for event/pitch/usagea.baurzhanov1
- 2026-09-23 15:28docs(readme): Baiterek v2 descriptionmiras
- 2026-09-23 15:28feat(ui): Baiterek v2 - detailed model and "Подъём на Байтерек" intromiras
- 2026-09-23 15:27merge: UI round 3 (event card, pitch button, usage panel) into maina.baurzhanov1
- 2026-09-23 15:22test: Playwright acceptance for the full jury patha.baurzhanov1
- 2026-09-23 15:21fix(ui): flag runs where the LLM did not answer; README options are implementedmiras
- 2026-09-23 15:18feat(ui): real city event and pitch export on the verdictmiras
- 2026-09-23 15:16Merge remote-tracking branch 'origin/main' into feat/uimiras
- 2026-09-23 15:16fix: keep public/ so the Docker build can copy ita.baurzhanov1
- 2026-09-23 15:14docs: three verification seeds with expected resultsa.baurzhanov1
- 2026-09-23 15:12fix(ui): leaderboard fits 1024-1280px, team names wrap within a min widthmiras
- 2026-09-23 15:12docs: backlog statuses, plan addendum for optimizer, events, pitcha.baurzhanov1
- 2026-09-23 15:11docs: cite the dataset's AI role requirement in READMEa.baurzhanov1
- 2026-09-23 15:10fix: optimizer cache keyed by dataset hash, finite-score guard, event EPS, pitch newline safety, turbopack ignorea.baurzhanov1
- 2026-09-23 15:07docs: test counta.baurzhanov1
- 2026-09-23 15:07docs: README and backlog for full optimizer, events, pitch exporta.baurzhanov1
- 2026-09-23 15:06feat: full optimizer with cache (694k sets), city events, pitch export, empty DATA_DIR fallback, run durationa.baurzhanov1
- 2026-09-23 15:01feat(ui): shock event card and LLM usage panel on the verdictmiras
- 2026-09-23 14:59chore: turbopack ignore for run store paths, CityEvent contract, refreshed screenshotsa.baurzhanov1
- 2026-09-23 14:58merge: UI round 2 (leaderboard, heat map, design pass) into maina.baurzhanov1
- 2026-09-23 14:57feat(ui): landing hero with interactive 3D Baiterek (three.js)miras
- 2026-09-23 14:55docs(readme): team section with Мирас Есеналы and UI contributionsmiras
- 2026-09-23 14:55style(ui): anti-slop polish pass and DESIGN.mdmiras
- 2026-09-23 14:54docs(readme): map features to the case brief, fix surname, update UI scopemiras
- 2026-09-23 14:50merge: main (consilium backend, integration fixes) into feat/uimiras
- 2026-09-23 14:47docs: README screenshots of the three screensa.baurzhanov1
- 2026-09-23 14:46feat(ui): schematic Astana district heat map on briefing and verdict (M9)miras
- 2026-09-23 14:46fix(ui): read runs from store, surface API errors, recover after SSE drop via early runId, non-fatal stage errorsa.baurzhanov1
- 2026-09-23 14:46merge: UI + integration fixes into maina.baurzhanov1
- 2026-09-23 14:41merge: UI (feat/ui) into maina.baurzhanov1
- 2026-09-23 14:41feat(ui): leaderboard page and runs list API (M8)miras
- 2026-09-23 14:40docs: README verification section with live numbers, plan sync, live run fixturea.baurzhanov1
- 2026-09-23 14:40merge: consilium backend (A5-A11) into maina.baurzhanov1
- 2026-09-23 14:39fix: strict schemas, reasoning effort, LLM fallbacks for reviewer/arbiter, arbiter number guard (review findings)a.baurzhanov1
- 2026-09-23 14:38feat(ui): live consilium page with stage progress and demo replay (M7, M6)miras
- 2026-09-23 14:37feat(ui): decision cabinet with live score and mandate prefill (M3, M6 prefill)miras
- 2026-09-23 14:36docs: backlog statuses after consilium builda.baurzhanov1
- 2026-09-23 14:35feat(ui): briefing, cabinet components, verdict and council hall (M1, M2, M4, M5)miras
- 2026-09-23 14:35feat: consilium pipeline — optimizer, experts, synthesizer, reviewers, arbiter, SSE API, JSON store (A5-A11)a.baurzhanov1
- 2026-09-23 14:34Merge remote-tracking branch 'origin/main' into feat/uimiras
- 2026-09-23 14:33fix: track src/lib/data (gitignore matched any data/ dir)a.baurzhanov1
- 2026-09-23 14:32fix: track src/lib/data (gitignore matched any data/ dir)a.baurzhanov1
- 2026-09-23 14:30chore: Dockerfile, compose, README draft; drop SQLite in favour of JSON runsa.baurzhanov1
- 2026-09-23 14:26Merge remote-tracking branch 'origin/main' into feat/uimiras
- 2026-09-23 14:26feat(ui): app shell, letterhead theme and route stubs (M0)miras
- 2026-09-23 14:25feat: llm wrapper and consilium module stubsa.baurzhanov1
- 2026-09-23 14:22feat: dataset, validator, engine and facts with golden tests (A2-A4)a.baurzhanov1
- 2026-09-23 14:19feat: shared types contract and sample run fixture (A1)a.baurzhanov1
- 2026-09-23 14:15chore: scaffold Next.js 16 app with shadcn, vitest and data deps (A0)a.baurzhanov1
- 2026-09-23 13:58docs: MVP spec, backlog and Claude Code scaffold for HackAlema.baurzhanov1
Структура репозитория (223 файлов)
.claude/agents/code-reviewer.md .claude/agents/codebase-explorer.md .claude/agents/ui-reviewer.md .claude/commands/handoff.md .claude/commands/review.md .claude/commands/task.md .claude/hooks/check-console-log.py .claude/project/conventions.md .claude/project/tech-stack.md .claude/settings.json .dockerignore .env.example .gitignore AGENTS.md CLAUDE.md DESIGN.md Dockerfile README.md components.json docker-compose.yml docs/DECISIONS.md docs/README.md docs/adr/README.md docs/backlog.md docs/plan.md docs/research.md docs/screenshots/briefing.png docs/screenshots/cabinet.png docs/screenshots/leaderboard.png docs/screenshots/verdict-full.png docs/screenshots/verdict.png docs/source/brief.md docs/source/dataset.md e2e/acceptance.spec.ts eslint.config.mjs fixtures/README.md fixtures/live-run.json fixtures/sample-run.json next.config.ts package.json playwright.config.ts postcss.config.mjs public/.gitkeep public/dataset-hackalem.json src/app/api/run/route.ts src/app/api/runs/[id]/event/route.ts src/app/api/runs/[id]/pitch/route.ts src/app/api/runs/[id]/route.ts src/app/api/runs/[id]/voice/route.ts src/app/api/runs/route.ts src/app/favicon.ico src/app/globals.css src/app/layout.tsx src/app/leaderboard/page.tsx src/app/page.tsx src/app/play/page.tsx src/app/result/[runId]/page.tsx src/app/result/live/page.tsx src/app/sandbox/page.tsx src/components/briefing/DistrictGrid.tsx src/components/briefing/DistrictTile.tsx src/components/briefing/TeamForm.tsx src/components/consilium/ArbiterResolution.tsx src/components/consilium/ConditionRow.tsx src/components/consilium/ConsiliumHall.tsx src/components/consilium/DisputeItem.tsx src/components/consilium/DraftHistory.tsx src/components/consilium/DraftList.tsx src/components/consilium/DraftText.tsx src/components/consilium/DraftView.tsx src/components/consilium/ExpertCard.tsx src/components/consilium/ExpertDetails.tsx src/components/consilium/ExpertLine.tsx src/components/consilium/ExpertPendingCard.tsx src/components/consilium/ExpertTable.tsx src/components/consilium/FactChip.tsx src/components/consilium/HallSection.tsx src/components/consilium/MandateItem.tsx src/components/consilium/ResolutionBrief.tsx src/components/consilium/ResolutionDetails.tsx src/components/consilium/ReviewRound.tsx src/components/consilium/ReviewerBoard.tsx src/components/consilium/conditions.ts src/components/consilium/highlight.ts src/components/landing/BaiterekCanvas.tsx src/components/landing/BaiterekScene.tsx src/components/landing/BaiterekSilhouette.tsx src/components/landing/HeightReadout.tsx src/components/landing/baiterek-base.ts src/components/landing/baiterek-camera.ts src/components/landing/baiterek-dims.ts src/components/landing/baiterek-model.ts src/components/landing/baiterek-post.ts src/components/landing/baiterek-sphere.ts src/components/landing/baiterek-stage.ts src/components/landing/baiterek-trunk.ts src/components/landing/spin-controls.ts src/components/leaderboard/LeaderboardRow.tsx src/components/leaderboard/LeaderboardTable.tsx src/components/live/LiveHall.tsx src/components/live/LiveMessage.tsx src/components/live/LiveRun.tsx src/components/live/StageList.tsx src/components/live/useLiveRun.ts src/components/map/DistrictHeatMap.tsx src/components/map/DistrictShape.tsx src/components/map/HeatLegend.tsx src/components/map/geometry.ts src/components/map/heat.test.ts src/components/map/heat.ts src/components/play/BudgetBar.tsx src/components/play/Cabinet.tsx src/components/play/DistrictPicker.tsx src/components/play/LiveScore.tsx src/components/play/MandateNotice.tsx src/components/play/MeasureCatalog.tsx src/components/play/MeasureDetails.tsx src/components/play/MeasureRow.tsx src/components/play/RowNotes.tsx src/components/play/SetPanel.tsx src/components/play/SlotList.tsx src/components/play/ValidationErrors.tsx src/components/play/useCabinet.ts src/components/result/DeltaValue.tsx src/components/result/Disclosure.tsx src/components/result/DistrictComparison.tsx src/components/result/DistrictSwitcher.tsx src/components/result/HowCalculated.tsx src/components/result/IndicatorTable.tsx src/components/result/MeasureContributions.tsx src/components/result/NextSteps.tsx src/components/result/OutcomeStamp.tsx src/components/result/QuarterSlider.tsx src/components/result/ScoreRadar.tsx src/components/result/ShockEventCard.tsx src/components/result/UsagePanel.tsx src/components/result/VerdictSummary.tsx src/components/result/VoiceButton.tsx src/components/sandbox/DatasetPreview.tsx src/components/sandbox/SandboxImport.tsx src/components/sandbox/SandboxNotice.tsx src/components/sandbox/useSandbox.ts src/components/shell/AppMark.tsx src/components/shell/AppShell.tsx src/components/shell/MainNav.tsx src/components/shell/PageHeader.tsx src/components/shell/SiteHeader.tsx src/components/ui/badge.tsx src/components/ui/button.tsx src/components/ui/card.tsx src/components/ui/progress.tsx src/components/ui/separator.tsx src/components/ui/tabs.tsx src/lib/consilium/arbiter.test.ts src/lib/consilium/arbiter.ts src/lib/consilium/experts.test.ts src/lib/consilium/experts.ts src/lib/consilium/fallback.ts src/lib/consilium/llm.ts src/lib/consilium/numbers.test.ts src/lib/consilium/numbers.ts src/lib/consilium/pipeline.test.ts src/lib/consilium/pipeline.ts src/lib/consilium/prompts.ts src/lib/consilium/reviewers.test.ts src/lib/consilium/reviewers.ts src/lib/consilium/synthesizer.ts src/lib/data/data.test.ts src/lib/data/districts.ts src/lib/data/index.ts src/lib/data/measures.ts src/lib/data/synergies.ts src/lib/data/weights.ts src/lib/dataset.test.ts src/lib/dataset.ts src/lib/engine/engine.test.ts src/lib/engine/engine.ts src/lib/engine/events.test.ts src/lib/engine/events.ts src/lib/engine/facts.ts src/lib/engine/index.ts src/lib/engine/optimizer.test.ts src/lib/engine/optimizer.ts src/lib/engine/sandbox.test.ts src/lib/engine/timeline.test.ts src/lib/engine/timeline.ts src/lib/engine/validator.ts src/lib/fixture.test.ts src/lib/pitch/pitch.test.ts src/lib/pitch/pitch.ts src/lib/smoke.test.ts src/lib/store/runs.test.ts src/lib/store/runs.ts src/lib/types.ts src/lib/ui/cabinet.test.ts src/lib/ui/cabinet.ts src/lib/ui/format.test.ts src/lib/ui/format.ts src/lib/ui/humanize.test.ts src/lib/ui/humanize.ts src/lib/ui/labels.ts src/lib/ui/llm-status.test.ts src/lib/ui/llm-status.ts src/lib/ui/pending-run.ts src/lib/ui/replay.test.ts src/lib/ui/replay.ts src/lib/ui/run-source.test.ts src/lib/ui/run-source.ts src/lib/ui/run-stream.test.ts src/lib/ui/run-stream.ts src/lib/ui/sandbox.ts src/lib/ui/scenario.test.ts src/lib/ui/scenario.ts src/lib/ui/team.ts src/lib/ui/timeline.test.ts src/lib/ui/timeline.ts src/lib/ui/verdict.test.ts src/lib/ui/verdict.ts src/lib/utils.ts src/lib/voice/voice.ts tsconfig.json vitest.config.ts yarn.lock
Ветки (4)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.


