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
This commit is contained in:
2026-05-27 03:36:25 +07:00
parent b980566288
commit 6c46b3043a
10 changed files with 320 additions and 7 deletions
+46 -4
View File
@@ -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` полностью. 1. Сначала прочитать `Agent/TASK.md` полностью.
2. После чтения сверять все решения с требованиями из задачи. 2. Прочитать `Agent/Task/TASK_TEMPLATE.md`.
3. Не реализовывать вариант B или любую функциональность, которой нет в `Agent/TASK.md`. 3. Прочитать актуальные задачи `Agent/Task/TASK-*.md`.
4. Не добавлять лишние архитектурные слои, если они не нужны для выполнения задачи. 4. После чтения сверять все решения с требованиями из задачи и конкретных task-файлов.
5. Приоритет: минимальная корректная реализация, чистый жизненный цикл, понятный код. 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`. - Не использовать `FindObjectOfType`.
- Не использовать `Singleton.Instance`. - Не использовать `Singleton.Instance`.
- Не хранить состояние в `static`. - Не хранить состояние в `static`.
+3 -3
View File
@@ -12,16 +12,16 @@
- Unity 2022 LTS+. - Unity 2022 LTS+.
- C#. - C#.
- Разрешено использовать VContainer, UniTask, UniRx/R3 или собственную реактивную реализацию. - Использовать VContainer, UniTask и UniRx.
- Опционально можно использовать Odin Inspector и DOTween. - Опционально можно использовать Odin Inspector и DOTween.
## Архитектурные требования ## Архитектурные требования
### Reactive ### Reactive
Можно использовать UniRx/R3 или собственный `ReactiveValue<T>`. Использовать UniRx для реактивных значений и подписок.
Если используется собственная реализация, ожидаемая сигнатура подписки: Если дополнительно понадобится собственная обертка `ReactiveValue<T>`, она должна поддерживать сигнатуру подписки:
```csharp ```csharp
IDisposable Subscribe(Action<T> cb, bool invokeImmediately = true) IDisposable Subscribe(Action<T> cb, bool invokeImmediately = true)
+36
View File
@@ -0,0 +1,36 @@
# TASK-0001: Composition Root и зависимости
## Цель
Настроить корневую точку композиции проекта через VContainer так, чтобы orchestration-логика жила в plain C# классах, а MonoBehaviour использовались только как View-компоненты сцены.
## Что сделать
- Создать `GameLifetimeScope`.
- Зарегистрировать сервисы и контроллеры через VContainer как managed dependencies.
- Зарегистрировать `BootstrapEntryPoint` как entry point приложения.
- Зарегистрировать `BootStatesController` как `IStatesController<BootStateCode>` и при необходимости как self.
- Зарегистрировать `SplashState`, `LoadState`, `MenuState`.
- Зарегистрировать scene View через `RegisterComponent(...)`.
- Зарегистрировать settings asset через `[SerializeField]` и `RegisterInstance(...)`.
## Технические требования
- Использовать VContainer.
- Сервисы регистрировать через `builder.Register<Impl>(Lifetime.Singleton).As<IInterface>()`.
- Не использовать `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(...)`, потому что это данные, а не сервис с жизненным циклом.
+34
View File
@@ -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<TEnum>` с методом `EnterStateAsync(TEnum code, CancellationToken ct)`.
- Реализовать `StatesController<TEnum>`.
- Создать `BootStateCode` для состояний `Splash`, `Load`, `Menu`.
- Создать `BootStatesController`, который собирает конкретные boot states и передает их в базовый controller.
## Технические требования
- Использовать UniTask для всех async-методов.
- Во все async-операции передавать `CancellationToken`.
- Контракт перехода должен быть строгим: сначала `ExitAsync` текущего state, затем `EnterAsync` нового state.
- `StatesController<TEnum>` не должен знать бизнес-логику 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.
+34
View File
@@ -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.
+33
View File
@@ -0,0 +1,33 @@
# TASK-0004: UI база и ViewModel слой
## Цель
Создать базовый UI слой, в котором View отвечает только за Unity-ссылки и биндинги, а логика находится во ViewModel или state/flow сервисах.
## Что сделать
- Создать `IUIViewModel`.
- Создать `UIView : MonoBehaviour` с методами `Initialize()` и `Release()`.
- Создать `UIView<TVm> : 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<TVm>`.
- ViewModel можно передать во View явно перед `Initialize()`.
- `Release()` безопасен при повторном вызове.
- View не вызывает `FindObjectOfType`, не хранит глобальное состояние и не управляет переходами state machine.
## Заметки
MonoBehaviour должны оставаться presentation layer. Это соответствует задаче: логика находится в VM или сервисах, View только показывает состояние и прокидывает UI-события.
+38
View File
@@ -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.
+35
View File
@@ -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.
+36
View File
@@ -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`.
- В документации указано фактически потраченное время или место для его заполнения.
## Заметки
Документы являются частью оценки. В них нужно показать понимание решений, а не просто перечислить файлы проекта.
+25
View File
@@ -0,0 +1,25 @@
# TASK-000X: Название задачи
## Цель
Кратко описать, какой результат должен появиться после выполнения задачи.
## Что сделать
- Описать конкретные действия, которые нужно выполнить.
- Не добавлять лишние пункты, не относящиеся к текущей задаче.
- Формулировать действия так, чтобы их можно было проверить после выполнения.
## Технические требования
- Указать обязательные технологии, ограничения и архитектурные правила для задачи.
- Если специальных требований нет, написать: `Нет дополнительных требований кроме Agent/TASK.md`.
## Критерии готовности
- Описать проверяемые признаки завершения задачи.
- Каждый критерий должен быть конкретным и однозначным.
## Заметки
Дополнительный контекст, пояснения и важные решения. Если заметок нет, написать: `Нет`.