From 6c46b3043a1846931d05d72137482bf2d15f720e Mon Sep 17 00:00:00 2001 From: Konstantin Dyachenko Date: Wed, 27 May 2026 03:36:25 +0700 Subject: [PATCH] feat: add agent tasks and update documentation - Create 7 new task files (TASK-0001 through TASK-0007) - Add task template file for future use - Update Agent.md with project information - Update TASK.md with task tracking --- Agent/Agent.md | 50 ++++++++++++++++++++++++++++++++++--- Agent/TASK.md | 6 ++--- Agent/Task/TASK-0001.md | 36 ++++++++++++++++++++++++++ Agent/Task/TASK-0002.md | 34 +++++++++++++++++++++++++ Agent/Task/TASK-0003.md | 34 +++++++++++++++++++++++++ Agent/Task/TASK-0004.md | 33 ++++++++++++++++++++++++ Agent/Task/TASK-0005.md | 38 ++++++++++++++++++++++++++++ Agent/Task/TASK-0006.md | 35 ++++++++++++++++++++++++++ Agent/Task/TASK-0007.md | 36 ++++++++++++++++++++++++++ Agent/Task/TASK_TEMPLATE.md | 25 +++++++++++++++++++ 10 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 Agent/Task/TASK-0001.md create mode 100644 Agent/Task/TASK-0002.md create mode 100644 Agent/Task/TASK-0003.md create mode 100644 Agent/Task/TASK-0004.md create mode 100644 Agent/Task/TASK-0005.md create mode 100644 Agent/Task/TASK-0006.md create mode 100644 Agent/Task/TASK-0007.md create mode 100644 Agent/Task/TASK_TEMPLATE.md diff --git a/Agent/Agent.md b/Agent/Agent.md index c12c384..cfd7c0e 100644 --- a/Agent/Agent.md +++ b/Agent/Agent.md @@ -10,16 +10,58 @@ Agent/TASK.md Именно этот файл считается основным описанием задачи, требований и ограничений. +## Папка с задачами + +Детальный план работ хранится в папке: + +```text +Agent/Task/ +``` + +Задачи должны называться строго по формату: + +```text +TASK-0001.md +TASK-0002.md +TASK-0003.md +``` + +Новые задачи создаются только со следующим свободным номером. Нельзя пропускать номера без причины и нельзя переиспользовать номер удаленной или завершенной задачи. + +Правила шаблона задач описаны в файле: + +```text +Agent/Task/TASK_TEMPLATE.md +``` + +Каждая новая задача обязана соблюдать этот шаблон. + ## Порядок работы 1. Сначала прочитать `Agent/TASK.md` полностью. -2. После чтения сверять все решения с требованиями из задачи. -3. Не реализовывать вариант B или любую функциональность, которой нет в `Agent/TASK.md`. -4. Не добавлять лишние архитектурные слои, если они не нужны для выполнения задачи. -5. Приоритет: минимальная корректная реализация, чистый жизненный цикл, понятный код. +2. Прочитать `Agent/Task/TASK_TEMPLATE.md`. +3. Прочитать актуальные задачи `Agent/Task/TASK-*.md`. +4. После чтения сверять все решения с требованиями из задачи и конкретных task-файлов. +5. Не реализовывать вариант B или любую функциональность, которой нет в `Agent/TASK.md` или `Agent/Task/TASK-*.md`. +6. Не добавлять лишние архитектурные слои, если они не нужны для выполнения задачи. +7. Приоритет: минимальная корректная реализация, чистый жизненный цикл, понятный код. + +## Правила заведения задач + +- Новые задачи создавать только в `Agent/Task/`. +- Имя файла должно быть `TASK-XXXX.md`, где `XXXX` — номер из четырех цифр. +- Заголовок задачи должен начинаться с того же номера, что и имя файла. +- Структура задачи должна соответствовать `Agent/Task/TASK_TEMPLATE.md`. +- В задаче должны быть разделы `Цель`, `Что сделать`, `Технические требования`, `Критерии готовности`, `Заметки`. +- Задача должна быть достаточно крупной, чтобы не дробить работу на слишком много файлов. +- Задача должна быть достаточно конкретной, чтобы по критериям готовности можно было проверить результат. +- Нельзя заводить задачи по варианту B. ## Ограничения +- Использовать VContainer для DI. +- Использовать UniTask для async-операций. +- Использовать UniRx для реактивных значений и подписок. - Не использовать `FindObjectOfType`. - Не использовать `Singleton.Instance`. - Не хранить состояние в `static`. diff --git a/Agent/TASK.md b/Agent/TASK.md index 836204a..65eb135 100644 --- a/Agent/TASK.md +++ b/Agent/TASK.md @@ -12,16 +12,16 @@ - Unity 2022 LTS+. - C#. -- Разрешено использовать VContainer, UniTask, UniRx/R3 или собственную реактивную реализацию. +- Использовать VContainer, UniTask и UniRx. - Опционально можно использовать Odin Inspector и DOTween. ## Архитектурные требования ### Reactive -Можно использовать UniRx/R3 или собственный `ReactiveValue`. +Использовать UniRx для реактивных значений и подписок. -Если используется собственная реализация, ожидаемая сигнатура подписки: +Если дополнительно понадобится собственная обертка `ReactiveValue`, она должна поддерживать сигнатуру подписки: ```csharp IDisposable Subscribe(Action cb, bool invokeImmediately = true) diff --git a/Agent/Task/TASK-0001.md b/Agent/Task/TASK-0001.md new file mode 100644 index 0000000..67e278a --- /dev/null +++ b/Agent/Task/TASK-0001.md @@ -0,0 +1,36 @@ +# TASK-0001: Composition Root и зависимости + +## Цель + +Настроить корневую точку композиции проекта через VContainer так, чтобы orchestration-логика жила в plain C# классах, а MonoBehaviour использовались только как View-компоненты сцены. + +## Что сделать + +- Создать `GameLifetimeScope`. +- Зарегистрировать сервисы и контроллеры через VContainer как managed dependencies. +- Зарегистрировать `BootstrapEntryPoint` как entry point приложения. +- Зарегистрировать `BootStatesController` как `IStatesController` и при необходимости как self. +- Зарегистрировать `SplashState`, `LoadState`, `MenuState`. +- Зарегистрировать scene View через `RegisterComponent(...)`. +- Зарегистрировать settings asset через `[SerializeField]` и `RegisterInstance(...)`. + +## Технические требования + +- Использовать VContainer. +- Сервисы регистрировать через `builder.Register(Lifetime.Singleton).As()`. +- Не использовать `RegisterInstance(...)` для сервисов и controller-объектов. +- `RegisterInstance(...)` использовать только для settings/config assets. +- Не использовать `FindObjectOfType`, `Singleton.Instance` и `static`-хранилища состояния. +- MonoBehaviour не должны содержать orchestration-логику boot flow. + +## Критерии готовности + +- В сцене есть один `GameLifetimeScope`. +- Все boot flow зависимости собираются через VContainer. +- View-компоненты подключаются через ссылки из сцены, а не через поиск объектов. +- Entry point запускается контейнером. +- Сервисы и state machine не создаются вручную через `new` внутри MonoBehaviour. + +## Заметки + +Scene View допустимо регистрировать как компоненты, потому что они физически живут в сцене. Settings asset допустимо регистрировать через `RegisterInstance(...)`, потому что это данные, а не сервис с жизненным циклом. diff --git a/Agent/Task/TASK-0002.md b/Agent/Task/TASK-0002.md new file mode 100644 index 0000000..666aed1 --- /dev/null +++ b/Agent/Task/TASK-0002.md @@ -0,0 +1,34 @@ +# TASK-0002: Базовая архитектура Boot Flow + +## Цель + +Создать минимальную архитектурную основу для boot flow: сервисный lifecycle, state-контракты и generic state controller. + +## Что сделать + +- Создать `IService` с методами `InitializeAsync(CancellationToken ct)` и `ReleaseAsync(CancellationToken ct)`. +- Создать базовый `Service : IService`, если это упростит общую структуру. +- Создать `IState` с методами `EnterAsync(CancellationToken ct)` и `ExitAsync(CancellationToken ct)`. +- Создать `IStatesController` с методом `EnterStateAsync(TEnum code, CancellationToken ct)`. +- Реализовать `StatesController`. +- Создать `BootStateCode` для состояний `Splash`, `Load`, `Menu`. +- Создать `BootStatesController`, который собирает конкретные boot states и передает их в базовый controller. + +## Технические требования + +- Использовать UniTask для всех async-методов. +- Во все async-операции передавать `CancellationToken`. +- Контракт перехода должен быть строгим: сначала `ExitAsync` текущего state, затем `EnterAsync` нового state. +- `StatesController` не должен знать бизнес-логику boot flow. +- State не должен сам решать, какой state будет следующим. + +## Критерии готовности + +- `EnterStateAsync` вызывает `ExitAsync` текущего state перед `EnterAsync` нового state. +- Первый вход в state работает без попытки выйти из отсутствующего текущего state. +- Повторный вход в другой state не оставляет предыдущий state активным. +- `CancellationToken` проброшен через `EnterStateAsync`, `ExitAsync` и `EnterAsync`. + +## Заметки + +Generic state machine должна уметь только переключать состояния. Сценарий `Splash -> Load -> Menu -> Load` должен жить отдельно, чтобы не смешивать control flow и инфраструктуру state machine. diff --git a/Agent/Task/TASK-0003.md b/Agent/Task/TASK-0003.md new file mode 100644 index 0000000..bad01ff --- /dev/null +++ b/Agent/Task/TASK-0003.md @@ -0,0 +1,34 @@ +# TASK-0003: BootFlowService и запуск сценария + +## Цель + +Реализовать внешний flow coordinator, который запускает и управляет сценарием загрузки приложения. + +## Что сделать + +- Создать `BootFlowService : Service` или сервис с аналогичным lifecycle. +- Реализовать сценарий `Splash -> Load -> Menu -> Load -> Menu ...`. +- Сделать так, чтобы `SplashState` выполнялся один раз при старте. +- После `MenuState` возвращаться в `LoadState` по restart-сигналу. +- Создать `BootstrapEntryPoint`, который запускается через VContainer entry point. +- Запустить `BootFlowService` из entry point через UniTask. +- Корректно обработать штатную отмену, не логируя ее как ошибку. + +## Технические требования + +- Использовать `IAsyncStartable` или подходящий VContainer entry point для запуска. +- Не запускать boot flow из `MonoBehaviour.Start()` вручную. +- Не делать самопереключающиеся states, которые вызывают `EnterStateAsync(...)` изнутри своего `EnterAsync(...)`. +- `OperationCanceledException` считать нормальным завершением при уничтожении scope или остановке flow. +- Все ожидания должны использовать токен, полученный сверху. + +## Критерии готовности + +- При старте выполняется последовательность `Splash -> Load -> Menu`. +- Нажатие `Restart` запускает новый цикл `Load -> Menu`. +- При отмене токена boot flow завершается без зависших UniTask. +- Orchestration-код находится в plain C# классе, не во View. + +## Заметки + +Рекомендуемый сценарий: внешний сервис вызывает `_states.EnterStateAsync(...)` последовательно. Это проще защищать на ревью и не создает проблем с reentrancy внутри states. diff --git a/Agent/Task/TASK-0004.md b/Agent/Task/TASK-0004.md new file mode 100644 index 0000000..87b7b90 --- /dev/null +++ b/Agent/Task/TASK-0004.md @@ -0,0 +1,33 @@ +# TASK-0004: UI база и ViewModel слой + +## Цель + +Создать базовый UI слой, в котором View отвечает только за Unity-ссылки и биндинги, а логика находится во ViewModel или state/flow сервисах. + +## Что сделать + +- Создать `IUIViewModel`. +- Создать `UIView : MonoBehaviour` с методами `Initialize()` и `Release()`. +- Создать `UIView : UIView where TVm : IUIViewModel`. +- Добавить явную привязку ViewModel во View через метод вроде `Bind(TVm vm)` или `Setup(TVm vm)`. +- Сделать `Release()` идемпотентным для всех View. +- Подготовить View для splash, loading и menu экранов. + +## Технические требования + +- ViewModel должна быть обычным C#-классом, не `MonoBehaviour`. +- Runtime-данные передавать во View явно, а не через DI-поля MonoBehaviour. +- View не должна содержать orchestration-логику boot flow. +- Все подписки и listeners должны сниматься в `Release()`. +- Повторный вызов `Release()` не должен приводить к ошибкам. + +## Критерии готовности + +- Есть базовые классы `UIView` и `UIView`. +- ViewModel можно передать во View явно перед `Initialize()`. +- `Release()` безопасен при повторном вызове. +- View не вызывает `FindObjectOfType`, не хранит глобальное состояние и не управляет переходами state machine. + +## Заметки + +MonoBehaviour должны оставаться presentation layer. Это соответствует задаче: логика находится в VM или сервисах, View только показывает состояние и прокидывает UI-события. diff --git a/Agent/Task/TASK-0005.md b/Agent/Task/TASK-0005.md new file mode 100644 index 0000000..d6d41c7 --- /dev/null +++ b/Agent/Task/TASK-0005.md @@ -0,0 +1,38 @@ +# TASK-0005: SplashState, LoadState и прогресс + +## Цель + +Реализовать splash и loading состояния с настоящей отменой async-операций и реактивным прогрессом через UniRx. + +## Что сделать + +- Реализовать `SplashState`. +- В `SplashState.EnterAsync` показать splash View и подождать 1 секунду через `UniTask.Delay(..., cancellationToken: ct)`. +- В `SplashState.ExitAsync` скрыть или release-нуть splash View. +- Реализовать `LoadState`. +- В `LoadState` создать реактивный прогресс от `0` до `1` через UniRx. +- Наружу отдавать progress как read-only значение или read-only интерфейс. +- В `LoadState.EnterAsync` сбрасывать progress в `0f`. +- Выполнить 5 шагов загрузки по 200 мс с обновлениями `0.2f`, `0.4f`, `0.6f`, `0.8f`, `1f`. +- Реализовать `LoadingUIView`, подписанный на progress. + +## Технические требования + +- Использовать UniTask и `CancellationToken` во всех delay. +- Использовать UniRx для progress и подписок. +- Подписки хранить в `CompositeDisposable` и очищать в `Release()`. +- `LoadingUIView` не должна менять progress, только читать его. +- Не обновлять progress UI через `Update()`. +- При использовании tween/lerp останавливать предыдущую визуализацию перед запуском новой. + +## Критерии готовности + +- `SplashState` отменяется через внешний `CancellationToken`. +- `LoadState` выставляет ожидаемую последовательность progress значений. +- `LoadingUIView` корректно обновляет progress bar. +- При выходе из `LoadState` подписки очищаются. +- Повторный вход в `LoadState` не создает дублирующихся подписок и не вызывает `NullReferenceException`. + +## Заметки + +В этом проекте важнее чистый lifecycle и гигиена подписок, чем сложная анимация progress bar. Достаточно прямого обновления или простого lerp. diff --git a/Agent/Task/TASK-0006.md b/Agent/Task/TASK-0006.md new file mode 100644 index 0000000..bc8697f --- /dev/null +++ b/Agent/Task/TASK-0006.md @@ -0,0 +1,35 @@ +# TASK-0006: MenuState и Restart + +## Цель + +Реализовать меню с кнопкой `Restart`, которое завершает `MenuState` и возвращает flow к загрузке. + +## Что сделать + +- Реализовать `MenuState`. +- Реализовать `MenuUIView`. +- Реализовать `MenuUIViewModel` или аналогичный plain C# объект для restart-сигнала. +- В `MenuState.EnterAsync` показать menu View и ожидать restart. +- Ожидание restart реализовать через `UniTaskCompletionSource` или аналогичный UniTask-friendly механизм. +- По нажатию кнопки `Restart` завершать ожидание `MenuState.EnterAsync`. +- В `MenuState.ExitAsync` release-нуть menu View и очистить подписки/listeners. + +## Технические требования + +- Кнопку подключать через `Button.onClick.AddListener(...)`. +- В `Release()` снимать ровно тот же listener через `RemoveListener(...)`. +- Не использовать `RemoveAllListeners()` как основной способ очистки. +- Не использовать анонимную лямбду, которую невозможно симметрично снять. +- Не переводить state machine напрямую из `MenuUIView`. +- Restart-событие должно попадать во flow через ViewModel/state, а не через глобальное состояние. + +## Критерии готовности + +- `MenuState.EnterAsync` завершается после нажатия `Restart`. +- После завершения `MenuState` внешний `BootFlowService` запускает `LoadState`. +- Несколько циклов `Menu -> Restart -> Load -> Menu` не дублируют callbacks. +- Повторный `Release()` у `MenuUIView` не падает. + +## Заметки + +Для кнопки лучше использовать method group или заранее сохраненный delegate. Это упрощает симметричный `AddListener`/`RemoveListener` и снижает риск дублирующихся callbacks. diff --git a/Agent/Task/TASK-0007.md b/Agent/Task/TASK-0007.md new file mode 100644 index 0000000..3921b9b --- /dev/null +++ b/Agent/Task/TASK-0007.md @@ -0,0 +1,36 @@ +# TASK-0007: Документация и проверка + +## Цель + +Подготовить проект к сдаче: описать запуск, собственные решения, использование AI и проверить основной сценарий. + +## Что сделать + +- Создать или обновить `README.md`. +- В `README.md` указать версию Unity, используемые пакеты и стартовую сцену. +- В `README.md` описать, как проверить цикл `Splash -> Load -> Menu -> Restart -> Load`. +- В `README.md` добавить 5-10 строк о том, что было бы доделано при наличии еще 2 часов. +- Создать `SELF_NOTES.md`. +- В `SELF_NOTES.md` объяснить ключевые архитектурные решения. +- Создать `AI_LOG.md`. +- В `AI_LOG.md` описать использование AI или честно указать, что AI не использовался. +- По возможности добавить несколько небольших Edit Mode или Play Mode тестов на pure C# слой. + +## Технические требования + +- В `SELF_NOTES.md` объяснить выбор внешнего flow coordinator вместо самопереключающихся states. +- В `SELF_NOTES.md` объяснить разделение plain C# orchestration и MonoBehaviour View. +- В `SELF_NOTES.md` описать гигиену подписок и повторных входов. +- Если тесты добавляются, не усложнять проект сверх задачи. + +## Критерии готовности + +- `README.md` содержит инструкцию запуска и сценарий проверки. +- `SELF_NOTES.md` содержит объяснение 2-3 ключевых решений. +- `AI_LOG.md` присутствует и честно описывает использование AI. +- Основной цикл вручную проверен: `Splash -> Load -> Menu -> Restart -> Load`. +- В документации указано фактически потраченное время или место для его заполнения. + +## Заметки + +Документы являются частью оценки. В них нужно показать понимание решений, а не просто перечислить файлы проекта. diff --git a/Agent/Task/TASK_TEMPLATE.md b/Agent/Task/TASK_TEMPLATE.md new file mode 100644 index 0000000..9cb8ac5 --- /dev/null +++ b/Agent/Task/TASK_TEMPLATE.md @@ -0,0 +1,25 @@ +# TASK-000X: Название задачи + +## Цель + +Кратко описать, какой результат должен появиться после выполнения задачи. + +## Что сделать + +- Описать конкретные действия, которые нужно выполнить. +- Не добавлять лишние пункты, не относящиеся к текущей задаче. +- Формулировать действия так, чтобы их можно было проверить после выполнения. + +## Технические требования + +- Указать обязательные технологии, ограничения и архитектурные правила для задачи. +- Если специальных требований нет, написать: `Нет дополнительных требований кроме Agent/TASK.md`. + +## Критерии готовности + +- Описать проверяемые признаки завершения задачи. +- Каждый критерий должен быть конкретным и однозначным. + +## Заметки + +Дополнительный контекст, пояснения и важные решения. Если заметок нет, написать: `Нет`.