| # target.md — краткая цель для Codex / Claude Code |
|
|
| ## 0. Режим работы агента |
|
|
| Прочитай этот документ и реализуй задачу шаг за шагом. Не пытайся угадать архитектуру: сначала найди реальные файлы проекта, затем вноси минимальные изменения. |
|
|
| Главный принцип: после каждого крупного блока запускай игру, смотри `logs/run_*/ledger.jsonl`, проверяй, как реально живут жители, и исправляй баги поведения. Зелёные тесты недостаточны: в демо жители должны жить, голосовать, копить ресурсы, взаимодействовать со странами и действовать через MCP. |
|
|
| --- |
|
|
| ## 1. Финальная цель |
|
|
| Сделать hackathon MVP игры: |
|
|
| - единый живой мир; |
| - две страны/фракции; |
| - NPC-жители внутри стран; |
| - внешний пользователь подключает своего агента через MCP как обычного жителя; |
| - публичных God Console, Chaos и Overseer больше нет; |
| - все публичные действия идут только через единый MCP-контракт; |
| - роли NPC не ограничивают доступные действия, а только влияют на контекст и стиль поведения; |
| - каждые N тиков проходят выборы; |
| - во время выборов появляется дополнительное действие `vote`; |
| - правитель задаёт текстовую политику страны; |
| - политика попадает в контекст NPC; |
| - у страны есть физическая казна; |
| - жители могут пополнять казну и воровать у другой страны; |
| - страна может скрафтить пушку; |
| - у пушки есть оператор; |
| - оператор может стрелять по координатам; |
| - всё работает через текущий Gradio-интерфейс; |
| - админ может смотреть логи общения с моделями в читаемом виде. |
|
|
| --- |
|
|
| ## 2. Что обязательно выпилить / выключить |
|
|
| ### 2.1. Overseer |
|
|
| В финальном режиме Overseer не нужен. |
|
|
| Нужно: |
|
|
| - выключить Overseer в runtime; |
| - убрать его из tick loop; |
| - убрать Overseer directives из perception; |
| - убрать панель мыслей Overseer из Gradio; |
| - новые логи не должны содержать активные `overseer_request` / `overseer_response`. |
| - удалить |
|
|
| Если полное удаление файлов рискованно — не удаляй физически, а выключи через config/env. |
|
|
| ### 2.2. God Console / Chaos |
|
|
| Публичной консоли бога быть не должно. |
|
|
| Нужно: |
|
|
| - убрать God Console из Gradio; |
| - убрать Chaos framing из текстов; |
| - отключить публичные god/chaos endpoints или сделать их admin-only; |
| - пользователь может только: |
| - наблюдать за миром; |
| - создать персонажа; |
| - подключить своего агента через MCP; |
| - проверить статус персонажа. |
|
|
| --- |
|
|
| ## 3. Главный продуктовый сценарий |
|
|
| Пользователь открывает Gradio-интерфейс и видит: |
|
|
| 1. **Observe the world** |
| Наблюдать за миром двух AI-стран. |
|
|
| 2. **Connect your agent** |
| Создать персонажа в выбранной стране и получить MCP token/URL. |
|
|
| 3. **Check your character** |
| Проверить статус персонажа по токену. |
|
|
| 4. **Admin logs / Admin panel** |
| Только для админа по `ADMIN_TOKEN`. |
|
|
| В мире должны быть видны: |
|
|
| - две страны; |
| - бейдж страны у NPC; |
| - роль NPC; |
| - текущий правитель страны; |
| - текущая политика страны; |
| - казна страны; |
| - пушка страны, если построена; |
| - ближайшие события. |
|
|
| --- |
|
|
| ## 4. MCP — единственный публичный способ действия |
|
|
| Все NPC и внешние агенты должны использовать один и тот же набор инструментов. Нельзя делать отдельные “магические” действия, которые обходят validator или напрямую меняют world state. |
|
|
| Инструменты доступны всем живым NPC и внешним агентам, если действие физически возможно. Роль не должна запрещать базовые действия. |
|
|
| Исключение: в день выборов появляется дополнительный инструмент `vote`. |
|
|
| --- |
|
|
| ## 5. Единый MCP-контракт |
|
|
| Оставь контракт в таком стиле. Это единственный публичный набор базовых действий. |
|
|
| ```python |
| ACTION_TOOLS: dict[str, dict[str, Any]] = { |
| "move": { |
| "description": ( |
| "Walk toward a destination position (x and z) this tick. To flee, " |
| "move toward coordinates away from the threat." |
| ), |
| "inputSchema": _obj( |
| { |
| "x": {"type": "number", "description": "Destination X coordinate."}, |
| "z": {"type": "number", "description": "Destination Z coordinate."}, |
| } |
| ), |
| }, |
| "speak": { |
| "description": ( |
| "Say a message. With target_npc_id it is direct speech to that NPC; " |
| "without one it is a shout heard by everyone nearby." |
| ), |
| "inputSchema": _obj( |
| { |
| "message": {"type": "string", "description": "What to say."}, |
| "target_npc_id": {"type": "string", "description": "Who to speak to (optional)."}, |
| }, |
| required=["message"], |
| ), |
| }, |
| "attack": { |
| "description": ( |
| "Attack a valid nearby threat or hostile target. Give target_entity_id " |
| "to choose a specific target, or omit it to attack the nearest valid threat. " |
| "The engine resolves approach, range and damage." |
| ), |
| "inputSchema": _obj( |
| {"target_entity_id": {"type": "string", "description": "Id of the target to attack."}} |
| ), |
| }, |
| "use": { |
| "description": ( |
| "Use something in the world: eat food, heal with herbs, gather from a resource node, " |
| "build or repair a house, use treasury, craft an object, or use a cannon. " |
| "The exact effect depends on target/use_type/params and is validated by the engine." |
| ), |
| "inputSchema": _obj( |
| { |
| "resource_type": { |
| "type": "string", |
| "enum": ["food", "herbs", "wood", "coins"], |
| "description": "Optional resource type for consuming, crafting or treasury actions.", |
| }, |
| "resource_id": {"type": "string", "description": "Resource node id, if using/gathering a resource."}, |
| "target_entity_id": {"type": "string", "description": "World object/entity id to use."}, |
| "use_type": { |
| "type": "string", |
| "description": "Use mode, e.g. eat, heal, gather, build, repair, craft, fire.", |
| }, |
| "params": { |
| "type": "object", |
| "description": "Additional structured parameters for the selected use_type.", |
| }, |
| } |
| ), |
| }, |
| "transfer": { |
| "description": ( |
| "Give a resource to a nearby NPC, treasury, storage or object. " |
| "Set take=true to steal/take from a valid nearby target if rules allow it." |
| ), |
| "inputSchema": _obj( |
| { |
| "target_npc_id": {"type": "string", "description": "The other NPC, if target is a character."}, |
| "target_entity_id": {"type": "string", "description": "Treasury/storage/object target id."}, |
| "resource_type": { |
| "type": "string", |
| "enum": ["food", "herbs", "wood", "weapon", "coins"], |
| }, |
| "amount": {"type": "integer", "minimum": 1, "maximum": 20}, |
| "take": {"type": "boolean", "description": "Steal/take instead of give."}, |
| }, |
| required=["resource_type", "amount"], |
| ), |
| }, |
| } |
| ``` |
|
|
| ### 5.1. Что делать с build/gather |
|
|
| Если в проекте уже есть отдельные `build` и `gather`, не обязательно ломать их сразу. Но публично и логически они должны быть приведены к `use`. |
|
|
| - `gather(resource_id)` → `use(resource_id=..., use_type="gather")` |
| - `build()` → `use(use_type="build")` |
| - `repair()` → `use(use_type="repair")` |
|
|
| Не добавляй отдельные новые MCP tools для каждой механики. Новые игровые механики должны проходить через `use` или `transfer`. |
|
|
| ### 5.2. Единственное исключение — vote |
|
|
| Во время выборов можно добавлять временный инструмент: |
|
|
| ```python |
| "vote": { |
| "description": ( |
| "Vote for a ruler candidate during an active election. " |
| "Use candidate_id from the current election candidate list." |
| ), |
| "inputSchema": _obj( |
| { |
| "candidate_id": { |
| "type": "string", |
| "description": "NPC id of the candidate." |
| } |
| }, |
| required=["candidate_id"], |
| ), |
| } |
| ``` |
|
|
| `vote` доступен только во время election window. В обычные тики его не должно быть в available tools. |
|
|
| --- |
|
|
| ## 6. Роли NPC |
|
|
| Сейчас классовая система ограничивает выбор действий. Это нужно убрать. |
|
|
| Роль должна быть только контекстом: |
|
|
| - Guard чаще защищает и атакует угрозы; |
| - Builder чаще строит и чинит; |
| - Gatherer чаще собирает ресурсы; |
| - Ruler чаще думает о политике страны; |
| - Cannon operator чаще управляет пушкой. |
|
|
| Но базовые MCP tools не должны блокироваться ролью. |
|
|
| Validator запрещает действие только по реальным причинам: |
|
|
| - NPC мёртв; |
| - цель мертва; |
| - цель далеко; |
| - нет ресурса; |
| - нет объекта; |
| - не хватает казны; |
| - не идёт голосование; |
| - NPC не ruler для смены политики; |
| - NPC не cannon operator для выстрела; |
| - cooldown; |
| - действие нарушает физику/правила мира. |
|
|
| --- |
|
|
| ## 7. Две страны |
|
|
| Добавить в world state две страны: |
|
|
| - `nemotron`; |
| - `qwen`. |
|
|
| У каждой страны: |
|
|
| - id; |
| - name; |
| - color/badge; |
| - citizens; |
| - ruler_id; |
| - policy; |
| - treasury; |
| - cannon, если построена; |
| - next_election_tick. |
| |
| У каждого NPC: |
| |
| - `country_id`; |
| - role; |
| - optional special status: `ruler`, `cannon_operator`; |
| - optional `model_profile_id`. |
|
|
| Perception каждого NPC должен содержать: |
|
|
| - свою страну; |
| - страну видимых NPC; |
| - текущего правителя своей страны; |
| - текущую политику своей страны; |
| - состояние казны; |
| - состояние пушки; |
| - ближайшие события. |
|
|
| --- |
|
|
| ## 8. Выборы |
|
|
| Выборы должны быть настраиваемыми через `.env`. |
|
|
| ```env |
| ELECTION_INTERVAL_TICKS=50 |
| ELECTION_DURATION_TICKS=10 |
| ``` |
|
|
| Если таких env нет — добавить. |
|
|
| ### Как должны работать выборы |
|
|
| 1. Каждые `ELECTION_INTERVAL_TICKS` начинается election window. |
| 2. Для каждой страны формируется список кандидатов из живых граждан. |
| 3. В perception добавляется: |
| - что идут выборы; |
| - список кандидатов; |
| - краткое описание каждого кандидата; |
| - что он делал; |
| - что говорил; |
| - значимые события. |
| 4. В available tools временно появляется `vote`. |
| 5. Каждый житель страны может проголосовать. |
| 6. Если NPC не проголосовал через модель — fallback должен проголосовать сам. |
| 7. Победитель становится ruler. |
| 8. Новый ruler задаёт policy страны через LLM или fallback. |
| 9. Policy сохраняется в CountryState и попадает в prompt граждан. |
|
|
| Важно: во время выборов NPC должен понимать, что сейчас политическая фаза, а не обычный survival tick. |
|
|
| --- |
|
|
| ## 9. Политика страны |
|
|
| Policy — короткое текстовое указание от правителя. |
|
|
| Примеры: |
|
|
| ```text |
| Protect the treasury, gather wood and avoid reckless war. |
| ``` |
|
|
| ```text |
| Steal coins from the other country and prepare to build a cannon. |
| ``` |
|
|
| ```text |
| Trade peacefully, help weak citizens and attack only if threatened. |
| ``` |
|
|
| Policy не должна напрямую менять мир. Она должна попадать в perception/prompt и влиять на выбор действий модели. |
|
|
| --- |
|
|
| ## 10. Казна |
|
|
| У каждой страны должна быть физическая казна на карте. |
|
|
| Казна хранит: |
|
|
| - food; |
| - herbs; |
| - wood; |
| - coins. |
|
|
| Действия: |
|
|
| - свой гражданин может положить ресурс в казну через `transfer`; |
| - свой гражданин может взять ресурс, если правила позволяют; |
| - гражданин другой страны может украсть ресурс через `transfer(take=true)`, если он рядом с казной; |
| - все treasury events должны попадать в ledger/engine events. |
|
|
| --- |
|
|
| ## 11. Пушка и крафт |
|
|
| Пушка — killer feature, но реализация должна быть простой. |
|
|
| ### Условия крафта |
|
|
| Через `.env`: |
|
|
| ```env |
| CANNON_MIN_POPULATION=6 |
| CANNON_TREASURY_COST=20 |
| CANNON_WOOD_COST=15 |
| CANNON_HP_TAX_PER_CITIZEN=5 |
| CANNON_DAMAGE=35 |
| CANNON_RADIUS=5 |
| CANNON_COOLDOWN_TICKS=20 |
| ``` |
|
|
| Пушка крафтится через: |
|
|
| ```text |
| use(use_type="craft", params={"recipe_id": "cannon"}) |
| ``` |
|
|
| Условия: |
|
|
| - инициатор — ruler; |
| - населения достаточно; |
| - в казне достаточно ресурсов; |
| - пушка ещё не построена; |
| - HP tax не должен сразу уничтожить страну. |
|
|
| ### Оператор |
|
|
| Ruler назначает оператора через: |
|
|
| ```text |
| use(use_type="assign_cannon_operator", params={"npc_id": "..."}) |
| ``` |
|
|
| ### Выстрел |
|
|
| Оператор стреляет через: |
|
|
| ```text |
| use(use_type="fire", target_entity_id="cannon_id", params={"x": 10, "z": -5}) |
| ``` |
|
|
| Эффект: |
|
|
| - проверка operator; |
| - проверка cooldown; |
| - area damage по живым целям в радиусе; |
| - friendly fire допустим, чтобы у решения была цена; |
| - событие пишется в ledger и отображается в UI. |
|
|
| --- |
|
|
| ## 12. Внешний MCP-персонаж |
|
|
| Пользователь через Gradio создаёт персонажа: |
|
|
| - name; |
| - icon; |
| - country. |
|
|
| Получает: |
|
|
| - token; |
| - MCP URL; |
| - character id; |
| - список tools. |
|
|
| Ограничения: |
|
|
| - максимум 1 world-changing action per tick; |
| - observe можно чаще, но с rate limit; |
| - если персонаж умер — write actions возвращают ошибку; |
| - invalid token — auth error; |
| - внешний агент не может менять модель; |
| - внешний агент не может вызывать God Console; |
| - внешний агент действует теми же tools, что и NPC. |
|
|
| --- |
|
|
| ## 13. Admin model switching |
|
|
| Админ может переключить модель любого NPC из списка в JSON config. |
|
|
| Не-админ не может. |
|
|
| Добавить или использовать: |
|
|
| ```env |
| ADMIN_TOKEN=dev-admin-token |
| MODEL_PROFILES_PATH=config/model_profiles.json |
| ``` |
|
|
| Минимальные endpoints: |
|
|
| ```text |
| GET /admin/models |
| POST /admin/npcs/{npc_id}/model |
| ``` |
|
|
| Защита: |
|
|
| ```text |
| X-Admin-Token: <ADMIN_TOKEN> |
| ``` |
|
|
| В ledger должно быть видно, какой model profile использовался для NPC request. |
|
|
| --- |
|
|
| ## 14. Admin Model Logs Page |
|
|
| Сделать админ-страницу/вкладку в Gradio для просмотра общения с моделями. |
|
|
| ### Backend |
|
|
| Добавить endpoints: |
|
|
| ```text |
| GET /admin/logs/runs |
| GET /admin/logs/runs/current/records?since_line=0&limit=200 |
| GET /admin/logs/runs/{run_id}/records?phase=&npc_id=&tick_from=&tick_to=&limit= |
| ``` |
|
|
| SSE optional. Если не успевает — polling. |
|
|
| Читать именно: |
|
|
| ```text |
| logs/run_<UTC timestamp>/ledger.jsonl |
| ``` |
|
|
| Читать построчно, не грузить весь файл. |
|
|
| Возвращать нормализованный формат: |
|
|
| ```json |
| { |
| "line": 123, |
| "tick": 45, |
| "phase": "npc_response", |
| "npc_id": "npc_7", |
| "model": "nemotron3-nano-4b-fp8", |
| "summary": "NPC used transfer to deposit wood; validator accepted", |
| "record": {} |
| } |
| ``` |
|
|
| ### Frontend / Gradio |
|
|
| Добавить вкладку: |
|
|
| ```text |
| Admin Logs |
| ``` |
|
|
| Показывать: |
|
|
| - список runs; |
| - фильтр phase; |
| - фильтр npc_id; |
| - tick range; |
| - accepted/repaired/rejected/fallback; |
| - live polling каждые 1–2 секунды; |
| - prompt payload pretty JSON; |
| - raw model output; |
| - parsed action; |
| - validator verdict; |
| - fallback reason; |
| - engine events. |
| |
| Если старые логи содержат Overseer — можно показывать как legacy. Новый режим не должен создавать новые Overseer records. |
| |
| --- |
| |
| ## 15. Gradio integration |
| |
| Всё должно работать через текущий Gradio-интерфейс. |
| |
| Нужно обновить: |
| |
| - landing text; |
| - Observe screen; |
| - Connect Agent; |
| - Check Character; |
| - Admin Logs; |
| - Admin model switch, если успеешь; |
| - country panels; |
| - NPC badges; |
| - treasury/cannon display. |
| |
| Не делай отдельный несовместимый UI, если основной демо-интерфейс — Gradio. |
| |
| --- |
| |
| ## 16. Приоритет реализации |
| |
| ### P0 — сначала |
| |
| 1. Найти реальные файлы tick/perception/validator/MCP/Gradio/ledger. |
| 2. Выключить Overseer. |
| 3. Выключить публичную God Console/Chaos. |
| 4. Зафиксировать MCP tools. |
| 5. Убрать role-based action blocking. |
| 6. Проверить, что игра запускается. |
| |
| ### P1 — ядро нового демо |
| |
| 7. Добавить две страны и `country_id`. |
| 8. Добавить country info в perception. |
| 9. Добавить country badges в Gradio. |
| 10. Добавить выборы через env. |
| 11. Добавить `vote` только во время election window. |
| 12. Добавить ruler. |
| 13. Добавить policy и передачу policy в prompt. |
|
|
| ### P2 — экономика и конфликт |
|
|
| 14. Добавить физическую казну. |
| 15. Реализовать deposit/steal через `transfer`. |
| 16. Добавить treasury events в ledger/UI. |
| 17. Добавить простую пушку. |
| 18. Реализовать craft через `use`. |
| 19. Реализовать assign operator через `use`. |
| 20. Реализовать fire через `use`. |
|
|
| ### P3 — админка |
|
|
| 21. Добавить admin model switching. |
| 22. Добавить admin logs backend. |
| 23. Добавить Admin Logs вкладку в Gradio. |
| 24. Добавить live polling логов. |
|
|
| ### P4 — self-play и полировка |
|
|
| 25. Запустить игру. |
| 26. Прогнать минимум 100–200 ticks. |
| 27. Проанализировать ledger. |
| 28. Исправить баги поведения. |
| 29. Повторить прогон. |
| 30. Подготовить короткий финальный отчёт. |
|
|
| --- |
|
|
| ## 17. Обязательный self-play workflow |
|
|
| После реализации каждого крупного блока: |
|
|
| 1. Запусти тесты. |
| 2. Запусти headless simulation. |
| 3. Запусти Gradio/server. |
| 4. Прогони 100–200 ticks. |
| 5. Найди latest `logs/run_*/ledger.jsonl`. |
| 6. Проверь: |
| - NPC понимают свою страну; |
| - NPC видят страну других; |
| - роли не блокируют базовые tools; |
| - во время выборов появляется `vote`; |
| - жители голосуют; |
| - ruler назначается; |
| - policy попадает в prompt; |
| - жители используют treasury; |
| - враги могут воровать; |
| - пушка достижима в demo config; |
| - dead NPC не появляются как targets; |
| - Overseer/God Console не активны; |
| - MCP-персонаж может действовать; |
| - accepted/repaired/rejected/fallback не деградировали. |
|
|
| Если поведение плохое — исправь prompt/perception/validator/balance и прогони снова. |
|
|
| --- |
|
|
| ## 18. Тесты |
|
|
| Добавить или обновить тесты: |
|
|
| - existing tests pass; |
| - Overseer disabled in final mode; |
| - public God Console disabled; |
| - MCP tools schema valid; |
| - roles do not block base tools; |
| - vote appears only during election; |
| - country_id in NPC perception; |
| - visible NPCs include country; |
| - election starts by env interval; |
| - fallback vote works; |
| - ruler assigned; |
| - policy appears in perception; |
| - treasury deposit works; |
| - treasury steal works; |
| - cannon craft rejects too early; |
| - cannon craft succeeds when requirements met; |
| - cannon fire damages area; |
| - dead MCP character cannot act; |
| - admin token required for admin endpoints; |
| - admin logs read ledger by line. |
| |
| Команды: |
| |
| ```bash |
| python -m pytest -q |
| python scripts/headless_sim.py |
| npm run typecheck |
| ``` |
| |
| Если используется Windows venv: |
| |
| ```bash |
| .venv\Scripts\python.exe -m pytest -q |
| .venv\Scripts\python.exe scripts\headless_sim.py |
| ``` |
| |
| --- |
| |
| ## 19. Definition of Done |
| |
| Готово, если: |
| |
| - Gradio запускается; |
| - публичного Overseer/God Console больше нет; |
| - есть две страны; |
| - NPC имеют country badges; |
| - MCP tools единые; |
| - роли не блокируют базовые действия; |
| - выборы идут по env interval; |
| - `vote` доступен только во время выборов; |
| - ruler задаёт policy; |
| - policy попадает в context NPC; |
| - treasury физически существует и используется; |
| - cannon можно скрафтить и выстрелить; |
| - external MCP character работает; |
| - admin model switching работает; |
| - admin logs page читает ledger; |
| - Codex сам прогнал игру и исправил найденные проблемы; |
| - тесты зелёные. |
| |
| --- |
| |
| ## 20. Финальный отчёт агента |
| |
| В конце напиши: |
| |
| ```text |
| Implemented: |
| - ... |
| |
| Validation: |
| - pytest: ... |
| - headless_sim: ... |
| - gradio/server: ... |
| - latest ledger: ... |
| - ticks simulated: ... |
|
|
| Observed behavior: |
| - countries: ... |
| - elections: ... |
| - policies: ... |
| - treasury: ... |
| - cannon: ... |
| - MCP character: ... |
|
|
| Known limitations: |
| - ... |
| ``` |
| |
| --- |
| |
| ## 21. Verification status (audit 2026-06-14, Claude) |
| |
| Проверено по коду / тестам / ledger. Статусы: `[x] VERIFIED` `[~] PARTIAL` `[!] BROKEN` `[ ] NOT IMPLEMENTED`. |
| |
| Self-play ledger: `logs/run_20260614T104917Z/ledger.jsonl` (200 тиков, routing Nemotron+Qwen, offline LLM-shaped completer гоняет тот же pipeline). Headless: `scripts/headless_sim.py` (300 тиков, deterministic). Tests: `pytest -q` → **116 passed**; `tests/test_country_systems.py` (7 новых). Frontend: `npm run typecheck` → clean. |
| |
| - [x] VERIFIED — Нет активных Overseer/God Console/Chaos в runtime. `api/runtime.py` tick() зовёт `plan_world_tick` без overseer; `overseer_mode="off"` (`spawning.py:81`); нет `/chaos`,`/god` роутов (`api/gradio_app.py`,`api/server.py`); фронт без Chaos/Overseer (ChaosDock/OverseerPanel удалены). |
| - [x] VERIFIED — Публичные действия только через MCP. `api/mcp_server.py` `assert set(ACTION_TOOLS)==set(PLAYER_ACTIONS)`; всё идёт через `runtime.player_act`→`queue_action`. |
| - [x] VERIFIED — Единые tools `move/speak/attack/use/transfer`. `mcp_server.py:51`, `players.py:38`. |
| - [x] VERIFIED — `vote` только во время выборов. `mcp_server` VOTE_TOOL за `player_vote_available`; коннектор добавляет `vote` лишь при `active_election_for_npc` (`openai_compatible.py:558`). Тест `test_vote_tool_only_during_election`. |
| - [x] VERIFIED — Роли не блокируют действия. Промпт «Your role is context, not a tool restriction»; `allowed=PRIMITIVE_TOOL_IDS` для всех; players `unrestricted_actions=True`. |
| - [x] VERIFIED — Две страны, одна на Qwen. `spawning.py` Nemotron+Qwen; Qwen NPC `connector_id="qwen"`→ qwen secondary connector (`game.modal.local.json`, model `qwen3.6-27b-h100-fp8`). Ledger: модели per-country (nemotron3-nano 534 / qwen3.6-27b 466). Тест `test_demo_config_spawns_two_populated_countries`. |
| - [x] VERIFIED — NPC знают свою/чужую страну, правителя, policy, казну, мир. `_build_survival_messages` (`country`+`world_countries`), `build_perception` + rival lines. Тест `test_survival_prompt_exposes_rival_countries`. |
| - [x] VERIFIED — LLM-жители реально играют (action/speak/move/vote/transfer). Self-play ledger: 1914 npc_request/response; actions use 724 / move 829 / transfer 179 / speak 94 / vote 27; engine events gather/treasury_deposit/vote_cast/cannon_*. (Modal-эндпоинты настроены; live-прогон не делал из-за latency, harness гоняет идентичный pipeline routing→ledger.) |
| - [x] VERIFIED — Ledger содержит npc_id, model/model_profile, request, raw response, parsed action, validator verdict, fallback reason, engine events. `connectors/openai_compatible.py` `_npc_request_entry`/`_npc_response_entry`; verdict реконсилится в `tick.py:_publish_plan_ledger_entries`. |
| - [x] VERIFIED — Для каждого жителя видно модель. npc_request несёт `model`+`model_profile`+`connector_id`; admin-нормализатор отдаёт `model` per NPC (qwen-001→qwen3.6-27b). |
| - [x] VERIFIED — Fallback явно виден. `npc_fallback` с `reason` (45 записей «model output was unusable»). Тест `test_unusable_model_output_is_logged_as_fallback`. |
| - [~] PARTIAL — Внешняя MCP-модель играет по тем же правилам. `players.py` гонит queued action через тот же survival pipeline (`unrestricted_actions`, `vote` gated, dead→error); покрыто `tests/test_players_mcp.py`. Не гонял живого внешнего агента end-to-end в этой сессии. |
| - [x] VERIFIED — Выборы настоящие. `_run_election_system`/`_start_election`/`_finish_election`/`_fallback_vote`/`_set_country_policy`. Self-play: election_started/completed 5, vote_cast 25, policy_updated 5. |
| - [x] VERIFIED — Казна и пушка не декоративны. transfer меняет ресурсы (deposit/steal — тесты `test_treasury_*`), пушка craft/assign/fire меняет HP и убивает. Self-play: treasury_deposit 71, cannon_crafted 2, cannon_operator_assigned 2, cannon_fired 10. |
| - [x] VERIFIED — Gradio demo без God/Overseer. `api/public_ui.py`: Observe / Connect your agent / Country, без chaos/god framing. |
| - [x] VERIFIED — Admin logs читают `ledger.jsonl` построчно и показывают LLM/fallback. `runtime.admin_log_records` (line-by-line, фильтры phase/npc_id/tick); smoke: модели per-country + fallback summaries. |
| |
| ### Баги, найденные и исправленные в этой сессии |
| 1. `RoutingWorldSimulator.propose_tick` терял `ledger_entries` → при Qwen-роутинге LLM-вызовы не попадали в ledger/admin. Fix: `connectors/routing.py` (мерж entries по owner). |
| 2. `npcs.count: 0` во всех survival-конфигах → пустой demo-мир. Fix: `config/game*.json` count=6 (12 жителей). |
| 3. В ledger не было видно модель жителя. Fix: `model_profile`/`connector_id`/`country_id` в `_npc_request_entry` + `_stamp_model_attribution`. |
| 4. `_optional_known_entity_id` знал только beast-id → `target_entity_id` казны/пушки обнулялся, deposit/steal через LLM деградировали. Fix: распознаём treasury/cannon/house id. |
| 5. `transfer(take=true)` к казне уходил в NPC-steal вместо казны. Fix: `_verb_for_action` — transfer к treasury всегда идёт в treasury-handler (он чтит `take`). |
| 6. Perception без чужих стран и без своей позиции → нельзя воровать/навигировать. Fix: `world_countries` + `own_state.position` + rival lines в `build_perception`. |
| 7. При сбое/непарсимом ответе модели verdict в `npc_response` реконсилился к «accepted» дет-фолбэка → фолбэк маскировался. Fix: `tick.py:_publish_plan_ledger_entries` реконсилит только `pending`; явный `rejected`+reason сохраняется (self-play теперь показывает rejected 48). |
| |
| ### Real-endpoint smoke (честно) |
| 2-тиковый прогон на реальных Modal-эндпоинтах (`logs/run_20260614T110045Z`): tick'и прошли (status 200), но `/v1/chat/completions` не ответил в окне сессии (health=200, но генерация холодная/недоступна) → все NPC ушли в deterministic fallback, что корректно залогировано (24 `npc_fallback`). Это инфра, не код: ID моделей и routing верные, offline LLM-shaped self-play гоняет идентичный pipeline и подтверждает поведение. |
| |