О проекте
Команда: Вайбкодеры
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Веб-симулятор управления Астаной: выбор 5 из 14 мероприятий по районам в бюджете 100, детерминированный расчёт Score до/после, альтернативные планы, AI-объяснение и 3D-сцена города; задеплоен на NVIDIA Brev.
Как устроено
Next.js-фронт отправляет выбор в C# Minimal API, который валидирует правила (ровно 5 мер, ≤2 из направления, стоимость ≤100), применяет эффекты с долей реализации (8−лаг)/8 и бонусы синергии, считает Score = 0,7×средний балл + 0,3×худший район − число показателей ниже 40. Поиск альтернатив перебирает замены одной меры под цели. В режиме live OpenAI Responses API лишь ранжирует проверенные сервером утверждения, тексты формирует сервер; при ошибке — шаблонное объяснение.
Ключевые решения
- детерминированная модель Score
- перебор замен одной меры
- ранжирование проверенных утверждений LLM
- fallback mock
Особенности
LLM не генерирует факты: сервер формирует каталог проверенных утверждений, модель только выбирает их приоритет; процедурная 3D-модель Астаны на Three.js, локализация RU/KZ/EN и публичный деплой.
Стек
Next.js 16 · React 19 · TypeScript · Three.js · C# .NET 8 ASP.NET Core Minimal API · Swagger · Docker Compose · nginx · NVIDIA Brev
Модели
OpenAI Responses API (gpt-4o-mini)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-4c816c70-team.
README
README.mdАким на 5 часов
Симулятор управления Астаной для спецтрека Astana Innovations: пять решений, бюджет 100, пять районов. Соберите план и узнайте, как он меняет качество жизни и положение самого слабого района.
Открыть демо · Swagger демо · Контракт API · Сторонние компоненты
Быстрый старт: после клонирования репозитория запустите docker compose up --build и откройте localhost:3000. По умолчанию используется mock: ключ OpenAI не нужен. Настройки и подробные команды — в разделах 4–7.
Документ описывает код репозитория, включая альтернативы, сравнение попыток и исправления клавиатурного фокуса из 7073164. Публичное демо может отставать: проверенная версия деплоя.
1. Описание и назначение
Пользователь выбирает ровно пять из 14 мероприятий на горизонте восьми кварталов. Все начинают с одного синтетического датасета организаторов: пять районов, доли населения и десять показателей по шкале 0–100. Большее значение всегда лучше.
Маршрут приложения:
- Обзор (
/): исходные показатели, бюджет, правила и модель города. - Решения (
/decisions): каталог, выбор районов, стоимость и причины недоступности мер. - Результат (
/results): Score до/после, районные баллы, изменения показателей, расходы, сильные стороны и риски. Здесь же доступны альтернативные планы и сравнение двух последних успешных попыток.
Интерфейс доступен на русском, казахском и английском, с темами «День», «Ночь», «Бумага» и поддержкой уменьшенного движения. Трёхмерная сцена включает Байтерек, EXPO, Хан Шатыр, мечеть, монумент и мосты. Геометрия и размещение условные: это иллюстрация, а не географическая карта или источник числовых показателей.
На узком экране район выбирается через карточки и схему, колонка 3D скрыта. На широком экране при экономии трафика или недоступном WebGL показывается подписанный снимок модели. Черновик плана сохраняется в текущей вкладке. После полной перезагрузки результат нужно рассчитать снова — серверной истории и базы данных в MVP нет.
2. Архитектура и расчёт
Браузер: Next.js / React
├─ GET /api/scenario
├─ POST /api/simulations/evaluate
└─ POST /api/simulations/alternatives
↓
C# Minimal API: проверка → расчёт → объяснение
├─ единый датасет и детерминированная модель
└─ mock или OpenAI Responses API с резервным mock
Браузер обращается к API через NEXT_PUBLIC_API_URL. Локально это прямые запросы на порт 8080 с CORS; на Brev nginx объединяет сайт и API под одним HTTPS-адресом. Next.js rewrites и отдельный API_INTERNAL_URL не нужны.
Только C# рассчитывает итоговые числа. Интерфейс предварительно считает стоимость и количество выбранных мер, а также вычитает полученные «после − до» для показа изменений.
Правила выбора: ровно пять уникальных мер, не более двух из одного направления, стоимость не выше 100, обязательный район для районной меры и отсутствие района для городской. Несовместимость проверяется сервером. Невалидный набор возвращает стабильный код ошибки без оценки.
Порядок расчёта:
- Применить эффекты мер с долей реализации
(8 − лаг) / 8, затем фиксированные бонусы синергии. - Ограничить показатели диапазоном 0–100.
- Рассчитать районные баллы по весам десяти показателей и средний городской балл по долям населения.
- Рассчитать
Score = 0,7 × средний балл + 0,3 × балл худшего района − количество значений показателей ниже 40по всем районам.
Промежуточные значения не округляются; API округляет числа ответа до двух знаков. Исходные данные находятся в ScenarioData.cs, алгоритм — в ScoreCalculator.cs.
Роль AI. В режиме live сервер отправляет проверенные утверждения в OpenAI Responses API. Модель определяет их приоритет; итоговые тексты, переводы и рекомендации формирует сервер. Модель не рассчитывает Score и не сочиняет показатели. В режиме mock, а также при ошибке или некорректном ответе модели, сервер возвращает шаблонное объяснение с теми же рассчитанными числами. UI различает «AI-анализ» и «Шаблонное объяснение». Свободный чат и поиск глобально оптимального плана в MVP не реализованы.
Язык объяснения задаётся Accept-Language: ru-RU / kk-KZ / en-US; фактический язык возвращается в explanationLocale и Content-Language. Смена языка интерфейса не вызывает платный запрос автоматически: объяснение обновляется отдельной кнопкой.
На результате кнопка поиска вызывает POST /api/simulations/alternatives: сервер перебирает замены одной меры по целям Score, помощь слабейшему району и экономия, проверяет допустимость и рассчитывает последствия. AI может выбрать вариант и аргументы из проверенного набора. Это ограниченный поиск, а не глобальная оптимизация. Совпавшие стратегии не выдаются за разные планы; отсутствие улучшения показывается явно. «Применить» переносит пять мер в конструктор, а повторная оценка выполняется отдельным нажатием. История двух успешных планов хранится в памяти вкладки и очищается при перезагрузке или сбросе.
backend/ .NET API, датасет, расчёт и серверные проверки
frontend/app/ страницы /, /decisions, /results
frontend/features/ обзор и 3D, конструктор, результат, альтернативы, состояние
frontend/components/ общие компоненты интерфейса
frontend/lib/ API-клиент, контракты, форматирование и локализация
frontend/public/ логотип, favicon, снимки модели
frontend/tests/ frontend-тесты
frontend/docs/reports/ отчёты UI, интеграции и готовности релиза
docs/api.md сетевой контракт и примеры
docs/reference/ исходные материалы задания
3. Технологии и команда
| Часть | Технологии |
|---|---|
| Frontend | Next.js 16.3.6, React 19.2.8, TypeScript, CSS Modules |
| Визуализация | Three.js 0.186, собственные процедурные модели и SVG |
| Backend | .NET 8 / ASP.NET Core Minimal API, Swashbuckle / Swagger |
| Объяснение | OpenAI Responses API через HttpClient; необязательный внешний сервис |
| Запуск | Docker Compose, Node.js 22 и .NET 8 контейнеры |
| Публикация | NVIDIA Brev, nginx, HTTPS Secure Link |
Фронтенд и интеграция — Hizeshi; C# API — Тамерлан (TamerlanWebd); сервер и DevOps — Кирилл. Зависимости, лицензии и назначение компонентов раскрыты в THIRD_PARTY.md.
4. Требования
Для запуска достаточно Docker Engine / Docker Desktop и Docker Compose v2, доступа к интернету при первой сборке и свободных портов 3000 и 8080. На Windows используйте WSL Ubuntu с включённой интеграцией Docker Desktop. Команды ниже предназначены для Bash в WSL/Linux.
Установка Node.js и .NET на хост не требуется для Docker. Для разработки и тестов без контейнеров нужны Node.js 22, npm и .NET 8 SDK. Для серверного compose-файла требуется Compose 2.24.4+, поддерживающий !override.
5. Установка
git clone https://github.com/BAITC-Hacks/hack-4c816c70-team.git
cd hack-4c816c70-team
cp .env.example .env
Настройки из примера запускают mock без ключей. Файл .env не добавляется в Git. Не копируйте в него чужой ключ и не передавайте секреты через NEXT_PUBLIC_*: эти переменные доступны браузеру.
6. Переменные окружения
| Переменная | Значение по умолчанию / назначение |
|---|---|
API_PORT | 8080, порт API на хосте |
FRONTEND_PORT | 3000, порт сайта на хосте |
GATEWAY_PORT | 8000, только для серверного nginx |
FRONTEND_ORIGIN | Пусто: http://localhost:<FRONTEND_PORT>; разрешённые CORS origins через запятую |
NEXT_PUBLIC_API_URL | http://localhost:8080; адрес API для браузера, встраивается при сборке |
ASPNETCORE_ENVIRONMENT | Production |
LLM_MODE | mock без внешних запросов; live для OpenAI |
OPENAI_API_KEY | Пусто; нужен только для live, только на сервере |
OPENAI_MODEL | Пусто; для live укажите доступную модель, команда использует gpt-4o-mini |
OPENAI_BASE_URL | Пусто: стандартный OpenAI API; можно задать совместимый адрес для тестирования |
При изменении API_PORT обновите и NEXT_PUBLIC_API_URL. После изменения адреса API необходима пересборка frontend. Для включения модели заполните серверные LLM_MODE=live, OPENAI_API_KEY, OPENAI_MODEL и пересоздайте API: docker compose up -d api.
7. Запуск и публикация
Из корня репозитория:
docker compose up --build
После сборки откройте сайт, Swagger и health. Обе службы должны стать healthy:
docker compose ps
curl --fail http://localhost:8080/health
docker compose logs --tail=100 api frontend
Команды проверки выполняйте во втором терминале. Для фонового запуска: docker compose up --build -d --wait. Остановка: docker compose down. Базы данных и тома с результатами нет.
На сервере Brev из согласованной рабочей копии запускайте:
docker compose -f docker-compose.yml -f docker-compose.brev.yml up --build -d --wait
В Secure Link укажите HTTP-порт 8000. nginx направляет /api/, /health, /swagger в C# API, остальные запросы — во frontend. Серверный compose задаёт относительный NEXT_PUBLIC_API_URL=/; временный Cloudflare-туннель для такого запуска не нужен. Порты 3000 и 8080 привязаны к loopback, HTTPS обслуживает Brev.
docker compose -f docker-compose.yml -f docker-compose.brev.yml ps
curl --fail http://127.0.0.1:8000/health
Обновляйте только чистую согласованную копию: git fetch origin, проверка изменений, git pull --ff-only, затем сборка. Записывайте SHA исходников и фактически запущенных образов обоих сервисов; git push сам по себе сайт не обновляет. Сохраняйте действующую серверную конфигурацию и права доступа. На текущем стенде API и frontend запускались из разных каталогов — не выполняйте обновление из случайного старого checkout с тем же Compose project. Точное состояние и оставшиеся шаги — в отчёте готовности.
После обновления проверьте, что Swagger содержит /api/simulations/alternatives. Если там есть только scenario и evaluate, работает старая версия API; обновление только frontend не включит альтернативы.
Если запуск не удался:
- Порт занят — измените порты в
.env, согласуйте адрес API и повторите сборку. Failed to fetch/ CORS — проверьте API из браузера и точныйFRONTEND_ORIGIN, включая схему и порт;localhostи127.0.0.1различаются.- Сайт показывает старую версию — проверьте SHA контейнера и пересборку, затем обновите страницу.
- Вместо 3D виден снимок — прочитайте подпись и попробуйте «Запустить 3D»; ограничения WebGL не мешают оценке плана.
- Модель недоступна — числовой результат сохраняется, источник объяснения меняется на
mock.
8. Проверка и демонстрация
Контрольный план: M7 → Нура, M8 → Нура, M10 → Нура, M12 → город, M5 → Сарыарка. Ожидаемый результат: 52,56 → 56,54, расходы 95, остаток 5.
curl --fail-with-body http://localhost:8080/api/simulations/evaluate \
-H 'Content-Type: application/json' \
-H 'Accept-Language: ru-RU' \
--data '{"choices":[{"measureId":"M7","districtId":"nura"},{"measureId":"M8","districtId":"nura"},{"measureId":"M10","districtId":"nura"},{"measureId":"M12"},{"measureId":"M5","districtId":"saryarka"}]}'
Повторите с kk-KZ и en-US: числа должны совпасть, язык объяснения — измениться. Невалидный набор:
curl -i http://localhost:8080/api/simulations/evaluate \
-H 'Content-Type: application/json' --data '{"choices":[]}'
Ожидается HTTP 400 с code: WRONG_CHOICE_COUNT, без Score. Остальные коды, схемы и примеры — в контракте API.
Для короткого демо откройте обзор, соберите контрольный план, покажите результат и изменения Нуры. Затем измените район M5 на Алматы и оцените заново: Score меняется на 56,58 при тех же расходах 95. Покажите переключение языка и честное обозначение источника объяснения.
Для демонстрации альтернатив вернитесь к исходному контрольному плану и выберите цель Score: лучшая найденная замена M5 Сарыарка → M3 Нура даёт 57,21 при расходах 100. Цель экономии предлагает M5 Сарыарка → M11 Нура: 56,69 при расходах 80, с потерями для Сарыарки. Примените вариант, явно оцените его и покажите сравнение попыток. Эти значения проверены на API из репозитория; доступность нового экрана на публичном демо зависит от версии деплоя.
| План | Расходы | Остаток | Score |
|---|---|---|---|
| Контрольный: M7, M8, M10 в Нуре; M12 город; M5 Сарыарка | 95 | 5 | 56,54 |
| В контрольном плане заменить M5 Сарыарка на M3 Нура | 100 | 0 | 57,21 |
| В контрольном плане заменить M5 Сарыарка на M11 Нура | 80 | 20 | 56,69 |
Автоматические проверки frontend:
cd frontend
npm ci
npm run lint
npm test
npm run build
npm run typecheck
cd ..
Проверки backend:
dotnet run --project backend/CitySimulator.RegressionTests -c Release
node backend/tests/api-regression.mjs http://localhost:8080
HTTP regression требует отдельный запущенный тестовый API в режиме mock и делает более 500 запросов; не направляйте его на публичное демо с платной моделью. Подробности — в инструкции тестов.
| Проверка | Подтверждённый результат |
|---|---|
Frontend на коде 7073164 | 74/74 теста, lint, production build и typecheck |
Клавиатура на 7073164, локально, 390 px | Добавление/удаление сохраняют фокус; первая, последняя и единственная строки; Escape возвращает фокус на корзину. Повторно проверено Claude 2 без POST |
| Новый backend, Docker | 45/45 C# и 45/45 HTTP-проверок, 400 наборов и 120 перестановок; альтернативы включены |
| Чистый Compose на Brev | 6929bf4, оба контейнера healthy, без ключей; этот прогон предшествовал последнему UI альтернатив |
| Полный сценарий из чистого клона, отчёт Тамерлана | 88a0c8c: 110/110 браузерных проверок; план → результат → альтернативы → применение → отдельная оценка → сравнение; mock без ключа |
| Последующая приёмка, отчёт Тамерлана | 7c0570e: 31/31 проверка пустого HTTP 503, повторов и мобильного обзора; на 7073164 повторно прошла production-сборка Docker |
| Настоящий AI, отчёт Тамерлана | Один запрос альтернатив: HTTP 200, recommendation.source=llm, исходный план 56,54 / 95 → совет 57,21 / 100 |
| Firefox 156 / Intel Arc | Живая 3D-сцена, выбор районов и возврат со страницы решений; публичный frontend 631efbb |
Подробности и границы проверки — в независимом отчёте, приёмке отчётов и финальной интеграции Тамерлана, включая машинные отчёты и скриншоты. Интегратор проверял Docker на Brev, Тамерлан — отдельный локальный Docker-стек; это разные стенды. Физические мобильные устройства и скринридер не проверены. Локальная приёмка не подменяет проверку нового публичного деплоя.
Готовность релиза и ограничения проверок · Отчёт по 3D · Отчёт по сценарию · Серверная регрессия
Авторы коммитов
История коммитов
- 2026-09-23 17:40feat(frontend): style district and language dropdown menusHizeshi
- 2026-09-23 17:35fix(frontend): increase alternative goal controls to 44pxHizeshi
- 2026-09-23 17:27Merge final integration acceptance reportsHizeshi
- 2026-09-23 17:27docs: finalize README with verified end-to-end workflow and release statusHizeshi
- 2026-09-23 17:22test: record final clean-clone browser and live AI acceptanceTamerlan
- 2026-09-23 17:16fix(frontend): preserve planner keyboard focus and review agent reportsHizeshi
- 2026-09-23 17:09docs: update README and verify integrated alternatives releaseHizeshi
- 2026-09-23 17:07Merge remote-tracking branch 'origin/main'Hizeshi
Все коммиты (80)
- 2026-09-23 17:06feat(frontend): connect plan alternatives and compare successful attemptsTamerlan
- 2026-09-23 17:06fix(frontend): refresh city previews and record final acceptanceHizeshi
- 2026-09-23 17:05Merge remote-tracking branch 'origin/main' into integration/logo-cityHizeshi
- 2026-09-23 17:05fix(frontend): preserve HTTP errors with empty response bodiesHizeshi
- 2026-09-23 17:02docs: reconcile component disclosures after team mergeTamerlan
- 2026-09-23 17:01feat(api): add validated alternative strategies and grounded adviceTamerlan
- 2026-09-23 17:01Merge remote-tracking branch 'origin/main' into HEADTamerlan
- 2026-09-23 16:58docs: prepare demo README and consolidate release checksHizeshi
- 2026-09-23 16:49fix(frontend): keep API error details out of localized copyHizeshi
- 2026-09-23 16:45fix(frontend): show city preview and recover 3D startupHizeshi
- 2026-09-23 16:40Merge remote-tracking branch 'origin/main'Hizeshi
- 2026-09-23 16:40fix(git): inspect pushes with unfetched remote baseHizeshi
- 2026-09-23 16:39feat(frontend): integrate city polish and localized API errorsHizeshi
- 2026-09-23 16:35Merge remote-tracking branch 'origin/main'Hizeshi
- 2026-09-23 16:34fix(frontend): guard explanation renderingHizeshi
- 2026-09-23 16:33Merge remote-tracking branch 'origin/main' into HEADTamerlan
- 2026-09-23 16:33feat(api): explain negative measure effects and compensationTamerlan
- 2026-09-23 16:24merge: sync latest team updates before publishing fixeskilka165
- 2026-09-23 16:22fix: harden simulation responses and add regression coveragekilka165
- 2026-09-23 16:21merge: include final localization browser checksHizeshi
- 2026-09-23 16:21merge: publish city branding with localized explanation workflowHizeshi
- 2026-09-23 16:17test: record localized explanation retry browser checksTamerlan
- 2026-09-23 16:14feat(frontend): complete city landmarks and visual identityHizeshi
- 2026-09-23 16:12feat(frontend): request localized explanations and preserve retry resultsTamerlan
- 2026-09-23 16:09merge: synchronize origin mainHizeshi
- 2026-09-23 16:06feat(frontend): negotiate explanation localeHizeshi
- 2026-09-23 16:06chore: guard personal commit authorship and attributionTamerlan
- 2026-09-23 16:06Merge remote-tracking branch 'origin/main'Hizeshi
- 2026-09-23 16:05chore: require team synchronization before deliveryHizeshi
- 2026-09-23 16:05feat(frontend): model Astana landmarks and river bridgesHizeshi
- 2026-09-23 16:00fix(api): constrain claim counts and localize decimal textTamerlan
- 2026-09-23 16:00feat(api): localize explanations with Accept-LanguageTamerlan
- 2026-09-23 16:00chore: use each contributor own git identityTamerlan
- 2026-09-23 15:59fix(frontend): keep locale when storage is unavailableHizeshi
- 2026-09-23 15:55feat(frontend): localize simulator in ru kk enHizeshi
- 2026-09-23 15:50chore: enforce repository owner attributionHizeshi
- 2026-09-23 15:41Merge remote-tracking branch 'origin/main'Codex
- 2026-09-23 15:40feat(frontend): harden demo flow and mobile polishCodex
- 2026-09-23 15:36test: accept routed UI and document ranking fallbackTamerlan
- 2026-09-23 15:32Merge remote-tracking branch 'origin/main'Codex
- 2026-09-23 15:32feat(frontend): integrate 3d city and mobile polishCodex
- 2026-09-23 15:30fix(api): rank verified claims instead of generating factsTamerlan
- 2026-09-23 15:30docs: record claim catalog verification and frontend integrationTamerlan
- 2026-09-23 15:27docs(frontend): record polish verificationCodex
- 2026-09-23 15:23feat(frontend): prepare 3d city polishCodex
- 2026-09-23 15:14Merge remote-tracking branch 'origin/main'Codex
- 2026-09-23 15:13test: record live browser and tunnel acceptanceTamerlan
- 2026-09-23 15:13Merge remote-tracking branch 'origin/main'Codex
- 2026-09-23 15:13docs(frontend): record redesign verificationCodex
- 2026-09-23 15:13chore: add verified NVIDIA Brev deploymentkilka165
- 2026-09-23 15:12feat(frontend): redesign routed simulatorCodex
- 2026-09-23 15:09fix(api): generate district-aware recommendations on serverTamerlan
- 2026-09-23 15:01docs: clarify successful live check after browser testsTamerlan
- 2026-09-23 15:00test: record browser acceptance and live AI verificationTamerlan
- 2026-09-23 14:47chore: wire live AI configuration and document API tunnelTamerlan
- 2026-09-23 14:43feat(frontend): support live explanation sourceCodex
- 2026-09-23 14:41merge: update origin main before publishCodex
- 2026-09-23 14:40fix(frontend): focus results and semantic page mainCodex
- 2026-09-23 14:38merge: integrate origin mainCodex
- 2026-09-23 14:38feat(frontend): add city overview and plannerCodex
- 2026-09-23 14:36feat(api): add live AI explanation with safe fallbackTamerlan
- 2026-09-23 14:34docs: complete README with launch checks and acceptance guideHizeshi
- 2026-09-23 14:26docs(frontend): note integration blockersCodex
- 2026-09-23 14:24docs(frontend): record integration verificationCodex
- 2026-09-23 14:24feat(frontend): integrate simulator API and resultsCodex
- 2026-09-23 14:08chore: add Docker Compose setup and integration guideHizeshi
- 2026-09-23 14:07docs(frontend): exclude Intel Playground and local GPU inferenceHizeshi
- 2026-09-23 14:06docs(frontend): align integration handoff with confirmed API contractHizeshi
- 2026-09-23 13:57docs(frontend): define architecture, UI contracts and three-agent work splitHizeshi
- 2026-09-23 13:53feat: implement scenario API and docker launchTamerlan
- 2026-09-23 13:35chore: normalize documentation file modesTamerlan
- 2026-09-23 13:34chore: scaffold backend and Next.js frontendTamerlan
Структура репозитория (243 файлов)
.env.example .githooks/check-personal-authorship.py .githooks/commit-msg .githooks/pre-commit .githooks/pre-merge-commit .githooks/pre-push .gitignore AGENTS.md CLAUDE.md README.md THIRD_PARTY.md backend/.dockerignore backend/CitySimulator.Api/ApiExamplesFilter.cs backend/CitySimulator.Api/CitySimulator.Api.csproj backend/CitySimulator.Api/Features/Analysis/AlternativeAdviceService.cs backend/CitySimulator.Api/Features/Analysis/AlternativeSelectionValidator.cs backend/CitySimulator.Api/Features/Analysis/AnalysisFacts.cs backend/CitySimulator.Api/Features/Analysis/ExplanationService.cs backend/CitySimulator.Api/Features/Analysis/ExplanationValidator.cs backend/CitySimulator.Api/Features/Analysis/LlmOptions.cs backend/CitySimulator.Api/Features/Analysis/OpenAiExplanationClient.cs backend/CitySimulator.Api/Features/Scenario/ScenarioData.cs backend/CitySimulator.Api/Features/Scenario/ScenarioEndpoints.cs backend/CitySimulator.Api/Features/Scenario/ScenarioModels.cs backend/CitySimulator.Api/Features/Simulation/AlternativeModels.cs backend/CitySimulator.Api/Features/Simulation/AlternativeSearch.cs backend/CitySimulator.Api/Features/Simulation/ChoiceValidator.cs backend/CitySimulator.Api/Features/Simulation/ExplanationBuilder.cs backend/CitySimulator.Api/Features/Simulation/ExplanationLocales.cs backend/CitySimulator.Api/Features/Simulation/ExplanationText.cs backend/CitySimulator.Api/Features/Simulation/RecommendationBuilder.cs backend/CitySimulator.Api/Features/Simulation/ReplacementAdvisor.cs backend/CitySimulator.Api/Features/Simulation/ScoreCalculator.cs backend/CitySimulator.Api/Features/Simulation/SimulationEndpoints.cs backend/CitySimulator.Api/Features/Simulation/SimulationModels.cs backend/CitySimulator.Api/Features/Simulation/TextNumberFormat.cs backend/CitySimulator.Api/Program.cs backend/CitySimulator.Api/Properties/launchSettings.json backend/CitySimulator.Api/appsettings.Development.json backend/CitySimulator.Api/appsettings.json backend/CitySimulator.RegressionTests/CitySimulator.RegressionTests.csproj backend/CitySimulator.RegressionTests/Program.cs backend/CitySimulator.RegressionTests/README.md backend/Dockerfile backend/README.md backend/scripts/verify-explanation-locales.py backend/tests/api-regression.mjs docker-compose.brev.yml docker-compose.yml docs/api.md docs/reference/challenge-task.docx docs/reference/district-dataset.docx docs/reports/bugfix-regression.md docs/reports/e2e.md docs/reports/e2e/desktop-1440-overview.png docs/reports/e2e/desktop-1440-result-llm.png docs/reports/e2e/desktop-1440-result.png docs/reports/e2e/desktop-evaluate-offline.png docs/reports/e2e/desktop-planner.png docs/reports/e2e/desktop-scenario-offline.png docs/reports/e2e/final-desktop-ru-alternatives.png docs/reports/e2e/final-mobile-kk-comparison.png docs/reports/e2e/live-kk-desktop-results.png docs/reports/e2e/locales-en-retry-error.png docs/reports/e2e/locales-kk-mobile-390.png docs/reports/e2e/mobile-390-overview.png docs/reports/e2e/mobile-390-planner.png docs/reports/e2e/mobile-390-result.png docs/reports/e2e/mobile-390-tray.png docs/reports/e2e/v3-desktop-decisions.png docs/reports/e2e/v3-desktop-overview.png docs/reports/e2e/v3-desktop-results.png docs/reports/e2e/v3-mobile-390-decisions.png docs/reports/e2e/v3-mobile-390-overview.png docs/reports/e2e/v3-mobile-390-results.png docs/reports/e2e/v3-mobile-390-tray.png docs/reports/final-browser-summary.json docs/reports/final-http503-summary.json docs/reports/final-integration.md docs/team.md frontend/.dockerignore frontend/.gitignore frontend/AGENTS.md frontend/Dockerfile frontend/README.md frontend/app/decisions/page.tsx frontend/app/error.tsx frontend/app/layout.tsx frontend/app/loading.tsx frontend/app/page.tsx frontend/app/results/page.tsx frontend/components/ui/AppHeader.module.css frontend/components/ui/AppHeader.tsx frontend/components/ui/Badge.module.css frontend/components/ui/Badge.tsx frontend/components/ui/BrandMark.tsx frontend/components/ui/Button.module.css frontend/components/ui/Button.tsx frontend/components/ui/LanguageSwitcher.module.css frontend/components/ui/LanguageSwitcher.tsx frontend/components/ui/Metric.module.css frontend/components/ui/Metric.tsx frontend/components/ui/Panel.module.css frontend/components/ui/Panel.tsx frontend/components/ui/Reveal.module.css frontend/components/ui/Reveal.tsx frontend/components/ui/Select.module.css frontend/components/ui/Select.tsx frontend/components/ui/Skeleton.module.css frontend/components/ui/Skeleton.tsx frontend/components/ui/ThemeProvider.tsx frontend/components/ui/ThemeSwitcher.module.css frontend/components/ui/ThemeSwitcher.tsx frontend/components/ui/cx.ts frontend/components/ui/index.ts frontend/components/ui/messages.ts frontend/components/ui/theme-store.ts frontend/docs/3d-and-polish.md frontend/docs/START-HERE.md frontend/docs/acceptance.md frontend/docs/architecture.md frontend/docs/backend-handoff.md frontend/docs/demo.md frontend/docs/design.md frontend/docs/localization.md frontend/docs/redesign.md frontend/docs/reports/alternatives-handoff.md frontend/docs/reports/architect-audit.md frontend/docs/reports/architect.md frontend/docs/reports/city-browser-fallback.md frontend/docs/reports/city-model.md frontend/docs/reports/claude-1.md frontend/docs/reports/claude-2.md frontend/docs/reports/clean-start.md frontend/docs/reports/custom-select.md frontend/docs/reports/deployment.md frontend/docs/reports/gpt.md frontend/docs/reports/localization-review.md frontend/docs/reports/release-readiness.md frontend/docs/reports/report-review.md frontend/docs/reports/screenshots/claude-1-city-day-1440.png frontend/docs/reports/screenshots/claude-1-city-night-1440.png frontend/docs/reports/screenshots/claude-1-home-mobile-390.png frontend/docs/tasks/claude-1-city-acceptance.md frontend/docs/tasks/claude-1.md frontend/docs/tasks/claude-2-release-acceptance.md frontend/docs/tasks/claude-2.md frontend/docs/tasks/gpt.md frontend/docs/tasks/locale-final-pass.md frontend/eslint.config.mjs frontend/features/alternatives/AlternativesPanel.tsx frontend/features/alternatives/alternatives-state.ts frontend/features/alternatives/alternatives.module.css frontend/features/alternatives/index.ts frontend/features/alternatives/labels.ts frontend/features/alternatives/loader.ts frontend/features/alternatives/messages.ts frontend/features/alternatives/plan-diff.ts frontend/features/alternatives/types.ts frontend/features/city-overview/AtlasSketch.tsx frontend/features/city-overview/CityHome.tsx frontend/features/city-overview/CityScene.tsx frontend/features/city-overview/DistrictAtlas.tsx frontend/features/city-overview/DistrictDetails.tsx frontend/features/city-overview/DistrictExplorer.tsx frontend/features/city-overview/atlas-layout.ts frontend/features/city-overview/city-landmarks.ts frontend/features/city-overview/city-overview.module.css frontend/features/city-overview/city-scene-engine.ts frontend/features/city-overview/city-scene.module.css frontend/features/city-overview/district-summary.ts frontend/features/city-overview/format.ts frontend/features/city-overview/home.module.css frontend/features/city-overview/index.ts frontend/features/city-overview/messages.ts frontend/features/city-overview/use-city-copy.ts frontend/features/planner/BudgetRibbon.tsx frontend/features/planner/DecisionTray.tsx frontend/features/planner/DistrictChoice.tsx frontend/features/planner/DistrictPicker.tsx frontend/features/planner/MeasureCard.tsx frontend/features/planner/Planner.tsx frontend/features/planner/index.ts frontend/features/planner/labels.ts frontend/features/planner/localize.ts frontend/features/planner/messages.ts frontend/features/planner/planner.module.css frontend/features/planner/selection-rules.test.ts frontend/features/planner/selection-rules.ts frontend/features/results/AttemptHistory.tsx frontend/features/results/DistrictComparison.tsx frontend/features/results/Explanation.tsx frontend/features/results/Results.tsx frontend/features/results/ScoreComparison.tsx frontend/features/results/index.ts frontend/features/results/messages.ts frontend/features/results/results.module.css frontend/features/simulator/AppShell.tsx frontend/features/simulator/DecisionsPage.tsx frontend/features/simulator/HomePage.tsx frontend/features/simulator/ResultsPage.tsx frontend/features/simulator/SimulationProvider.tsx frontend/features/simulator/error-messages.ts frontend/features/simulator/routes.module.css frontend/features/simulator/simulator-reducer.ts frontend/features/simulator/simulator.module.css frontend/features/simulator/use-simulation.ts frontend/lib/api/adapter.ts frontend/lib/api/alternatives.ts frontend/lib/api/client.ts frontend/lib/api/errors.ts frontend/lib/api/payload.ts frontend/lib/api/request.ts frontend/lib/contracts/api.generated.ts frontend/lib/contracts/ui.ts frontend/lib/format/numbers.ts frontend/lib/i18n/LocaleProvider.tsx frontend/lib/i18n/catalog.ts frontend/lib/i18n/index.ts frontend/lib/i18n/locale-store.ts frontend/mocks/scenario.ts frontend/next.config.ts frontend/package-lock.json frontend/package.json frontend/public/city/akim-logo.png frontend/public/city/akim-logo.svg frontend/public/city/preview-day.png frontend/public/city/preview-night.png frontend/public/favicon.ico frontend/styles/globals.css frontend/styles/tokens.css frontend/tests/alternatives-api.test.ts frontend/tests/alternatives.test.ts frontend/tests/api-contract.test.ts frontend/tests/applied-effects.test.ts frontend/tests/explanation-locale.test.ts frontend/tests/i18n-catalog.test.ts frontend/tests/locale-store.test.ts frontend/tests/number-format.test.ts frontend/tests/simulation-error-messages.test.ts frontend/tests/simulator-reducer.test.ts frontend/tsconfig.json frontend/tsconfig.test.json
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.