From 963058806ea1ca1f43985e61c5873064b85fc94a Mon Sep 17 00:00:00 2001 From: ScuroNeko Date: Sat, 28 Mar 2026 12:52:57 +0000 Subject: [PATCH] Refresh scene wiki status Align Scenes pages with current API Mark scene backlog as work in progress --- Framework-Backlog-RU.md | 33 +++++++----- Framework-Backlog.md | 33 +++++++----- Scenes-RU.md | 110 +++++++++++++++++++--------------------- Scenes.md | 110 +++++++++++++++++++--------------------- 4 files changed, 144 insertions(+), 142 deletions(-) diff --git a/Framework-Backlog-RU.md b/Framework-Backlog-RU.md index 95b3cd1..0d12fb1 100644 --- a/Framework-Backlog-RU.md +++ b/Framework-Backlog-RU.md @@ -10,7 +10,7 @@ Current state: - Фреймворк хорошо обрабатывает один update через commands, payloads, middleware и update handlers. - В нём уже есть полезные низкоуровневые строительные блоки: `MsgContext`, drafts, payload routing, plugins и update handlers. -- Но пока нет first-class концепции для долгоживущих пользовательских interaction flows. +- Теперь в нём уже есть work-in-progress skeleton для долгоживущих interaction flows: сцены можно регистрировать в plugins, запускать через `MsgContext`, сохранять через `SessionStore` и маршрутизировать раньше обычной обработки команд. Why this matters: @@ -18,22 +18,27 @@ Why this matters: - Реальным ботам часто нужны концепции вроде "подождать следующее сообщение пользователя", "пользователь сейчас на шаге 3 из 5" или "нажатие кнопки переводит пользователя в следующее состояние сцены". - Без scene model пользователи библиотеки начинают строить свой mini-framework поверх Laniakea. -What is missing: +What is already present: -- Способ маршрутизировать updates в активную сцену до обычного command routing. -- Способ хранить conversation state на пользователя или чат. -- Способ описывать шаги и переходы без ручного state machine вокруг middleware и storage. -- Способ явно входить в flow, продолжать его, отменять и завершать. -- Способ поддерживать modal chat flows, когда пользователь "внутри" сцены, а обычный текст считается scene input, пока явная escape-команда не завершит режим. +- Маршрутизация активной сцены раньше обычного command flow. +- Session scopes на пользователя, чат и пару пользователь-чат. +- Явный вход и выход через `MsgContext`. +- Step handlers, scene-local commands и `OnMessage(...)`. +- In-memory session storage по умолчанию плюс интерфейс `SessionStore` для кастомного persistence. -Possible API direction: +What is still missing or not yet settled: -- Концепции `Scene`, `Step` и `SessionStore`. -- `bot.AddScene(...)` или отдельный scene registry. -- `ctx.Scene()`, `ctx.NextStep(...)`, `ctx.ExitScene()` или похожие helpers для state transitions. -- Routing rule: active scene first, then normal command/payload flow if no scene claims the update. +- Scene-local payload routing. +- Ясное решение о том, нужен ли вообще дополнительный публичный API для inspection сцен. +- Более широкая стабилизация и документация вокруг helper-методов для scene state. + +Current API direction: + +- `Scene`, `SceneContext`, `SceneSession` и `SessionStore`. +- `Plugin.NewScene(...)` и `Plugin.AddScene(...)`. +- `MsgContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene(...)`. +- `SceneContext.Stay()`, `Next(...)`, `Exit()`, `Pass()`, `BindData(...)` и `SaveData(...)`. - Storage-backed состояние на пользователя или чат с чистым интерфейсом для кастомного persistence. -- Локальные escape и passthrough-команды сцены, чтобы flow вроде `/startrp` могли переводить пользователя в отдельный chat mode, где большинство сообщений уходят прямо в сцену, а команды вроде `/exit` или небольшой whitelist сохраняют специальное поведение. Important design constraints: @@ -43,7 +48,7 @@ Important design constraints: Practical target: -- Сделать stateful bot flows first-class framework-supported паттерном вместо userland convention. +- Достабилизировать текущий scene skeleton до состояния first-class framework-supported паттерна. - Покрыть и step-based формы, и mode-based chat flows без необходимости строить пользовательские routing layers вокруг активных sessions. ## Secondary Backlog diff --git a/Framework-Backlog.md b/Framework-Backlog.md index be4da9e..532353d 100644 --- a/Framework-Backlog.md +++ b/Framework-Backlog.md @@ -10,7 +10,7 @@ Current state: - The framework is strong at handling a single update through commands, payloads, middleware, and update handlers. - It already has useful lower-level building blocks such as `MsgContext`, drafts, payload routing, plugins, and update handlers. -- It does not yet provide a first-class concept for long-lived user interaction flows. +- It now provides a work-in-progress scene skeleton for long-lived user interaction flows: scenes can be registered in plugins, entered through `MsgContext`, persisted through `SessionStore`, and routed before normal command handling. Why this matters: @@ -18,22 +18,27 @@ Why this matters: - Real bots often need concepts like "wait for the user's next message", "user is currently on step 3 of 5", or "button press moves the user to the next scene state". - Without a scene model, library users end up building their own mini-framework on top of Laniakea. -What is missing: +What is already present: -- A way to route updates to an active scene before normal command routing. -- A way to persist conversation state per user or per chat. -- A way to describe steps and transitions without hand-rolling state machines around middleware and storage. -- A way to enter, continue, cancel, and complete a conversation flow explicitly. -- A way to support modal chat flows where the user is "inside" a scene and ordinary text is treated as scene input until an explicit escape command exits the mode. +- Active-scene routing before normal command flow. +- Per-user, per-chat, and per-user-chat session scopes. +- Explicit scene entry and exit through `MsgContext`. +- Step handlers, scene-local commands, and `OnMessage(...)`. +- In-memory session storage by default, plus the `SessionStore` interface for custom persistence. -Possible API direction: +What is still missing or not yet settled: -- `Scene`, `Step`, and `SessionStore` concepts. -- `bot.AddScene(...)` or a dedicated scene registry. -- `ctx.Scene()`, `ctx.NextStep(...)`, `ctx.ExitScene()`, or similar state-transition helpers. -- Routing rule: active scene first, then normal command/payload flow if no scene claims the update. +- Scene-local payload routing. +- A clear decision on whether any additional public scene-inspection API is needed. +- Broader stabilization and documentation around the scene-state helper surface. + +Current API direction: + +- `Scene`, `SceneContext`, `SceneSession`, and `SessionStore`. +- `Plugin.NewScene(...)` and `Plugin.AddScene(...)`. +- `MsgContext.EnterScene(...)`, `EnterSceneStep(...)`, and `ExitScene(...)`. +- `SceneContext.Stay()`, `Next(...)`, `Exit()`, `Pass()`, `BindData(...)`, and `SaveData(...)`. - Storage-backed per-user or per-chat state with a clean interface for custom persistence. -- Scene-local escape and passthrough commands, so flows like `/startrp` can put a user into a dedicated chat mode where most messages go straight to the scene, while commands like `/exit` or a small whitelist still retain special meaning. Important design constraints: @@ -43,7 +48,7 @@ Important design constraints: Practical target: -- Make stateful bot flows a first-class, framework-supported pattern instead of a userland convention. +- Stabilize the current scene skeleton into a first-class framework-supported pattern. - Cover both step-based forms and mode-based chat flows without forcing users to build custom routing layers around active sessions. ## Secondary Backlog diff --git a/Scenes-RU.md b/Scenes-RU.md index 0d1dda7..f35e49a 100644 --- a/Scenes-RU.md +++ b/Scenes-RU.md @@ -1,8 +1,8 @@ # Scenes -DRAFT: эта страница описывает предлагаемую модель сцен и пока не соответствует реализованному API. +Scenes — это work-in-progress слой stateful-маршрутизации для долгоживущих диалогов в Laniakea. Базовый скелет уже есть в библиотеке: сцены можно регистрировать в plugins, запускать через `MsgContext`, сохранять через `SessionStore` и маршрутизировать раньше обычных команд. Эта страница описывает текущий API, уже работающие части и зоны, которые пока намеренно остаются незавершёнными. -Сцены — это планируемый stateful-слой маршрутизации для долгоживущих диалогов в Laniakea. Он должен располагаться поверх обычной маршрутизации команд и payload: если пользователь или чат находится внутри активной сцены, сцена получает update первой и решает, обработать его, продолжить состояние, выйти или передать управление обратно обычному plugin flow. +Сцены располагаются поверх обычной маршрутизации команд и payload: если пользователь или чат находится внутри активной сцены, сцена получает update первой и решает, обработать его, продолжить состояние, выйти или передать управление обратно обычному plugin flow. ## Зачем нужны сцены @@ -35,7 +35,7 @@ DRAFT: эта страница описывает предлагаемую мо Сессионная модель не должна предполагать, что "один пользователь" всегда является правильной единицей. Для Telegram у личных чатов и групп разные потребности. -Предлагаемые scope: +Текущие scope: - `SceneScopeUser`: одна сессия на пользователя во всех чатах. - `SceneScopeChat`: одна сессия на чат. @@ -47,7 +47,7 @@ DRAFT: эта страница описывает предлагаемую мо - `SceneScopeUser` оставлять для редких account-level flow, которые специально должны жить между чатами. - `SceneScopeChat` использовать только для общих room-level сценариев. -## Предлагаемые типы +## Текущие типы ```go type SceneScope int @@ -65,25 +65,27 @@ type SceneSession struct { } type SessionStore interface { - Get(key string) (*SceneSession, error) + Get(key string) (SceneSession, error) Set(key string, session SceneSession) error Delete(key string) error } ``` -## Предлагаемая регистрация сцен +Laniakea уже поставляется с `MemorySessionStore` по умолчанию, а `Bot.SetSessionStore(...)` позволяет заменить его на кастомный store. -Сцены стоит регистрировать внутри plugin в том же стиле, что и команды с payload. +## Текущая регистрация сцен + +Сцены регистрируются внутри plugin в том же стиле, что и команды с payload. ```go plugin.NewScene("rp"). SetScope(laniakea.SceneScopeUserChat). SetEntry("chat"). OnMessage(handleRPMessage). - AddCommand("rpstop", stopRP) + OnCommand("rpstop", stopRP) ``` -`SetEntry(...)` задаёт начальный step или state сцены. Когда `ctx.EnterScene("rp")` создаёт новую сессию, фреймворк записывает это значение в `SceneSession.Step`. +`SetEntry(...)` задаёт начальный step или state сцены. `ctx.EnterScene("rp")` теперь валидирует, что entry-step задан и зарегистрирован, до того как создать сессию. Так plugin API остаётся визуально согласованным: @@ -92,9 +94,9 @@ plugin.NewScene("rp"). - `AddUpdateHandler(...)` - `NewScene(...)` -## Режимы сцен +## Формы сцен -Для первой версии достаточно покрыть две самые частые формы. +Текущий скелет уже покрывает две самые частые формы. - Пошаговые сцены: именованный step обрабатывает каждый update и выбирает следующий step. - Модальные сцены: долгоживущий "режим" обрабатывает обычные сообщения, пока пользователь явно не выйдет. @@ -106,8 +108,8 @@ plugin.NewScene("rp"). plugin.NewScene("profile"). SetScope(laniakea.SceneScopeUserChat). SetEntry("name"). - AddStep("name", askName). - AddStep("confirm", confirmProfile) + OnStep("name", askName). + OnStep("confirm", confirmProfile) ``` Пример модального flow: @@ -117,36 +119,35 @@ plugin.NewScene("rp"). SetScope(laniakea.SceneScopeUserChat). SetEntry("chat"). OnMessage(handleRPMessage). - AddCommand("rpstop", stopRP) + OnCommand("rpstop", stopRP) ``` -## Предлагаемые контексты и результаты +## Текущие контексты и результаты -Обычные команды должны и дальше использовать `*MsgContext`. Scene handlers лучше давать отдельный wrapper, который встраивает обычный контекст и добавляет state information. +Обычные команды по-прежнему используют `*MsgContext`. Scene handlers используют `*SceneContext`, который встраивает `*MsgContext` и добавляет helper-методы для состояния сцены. ```go type SceneContext struct { *MsgContext - Scene string - Step string + // internal session state } -type SceneResult interface{ isSceneResult() } - -type SceneStay struct{} -type SceneNext struct{ Step string } -type SceneExit struct{} -type ScenePass struct{} +type SceneResult struct { + Action SceneAction + Next string +} ``` -Возможные helper-методы на `SceneContext`: +Текущие helper-методы на `SceneContext`: - `ctx.Stay()` - `ctx.Next(step)` - `ctx.Exit()` - `ctx.Pass()` +- `ctx.BindData(&dst)` +- `ctx.SaveData(src)` -## Предлагаемые сигнатуры handlers +## Сигнатуры handlers Обычная команда входа: @@ -182,13 +183,12 @@ func stopRP(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) { Для реальных сценариев недостаточно хранить только `Scene` и `Step`. Обычно между update нужно накапливать данные: черновик профиля, выбранные параметры, временные ID и другие промежуточные значения. -Это не стоит полностью перекладывать на разработчика приложения. Фреймворк должен давать слот для хранения в `SceneSession` и удобные helper-методы на `SceneContext`. +Это уже не стоит полностью перекладывать на разработчика приложения. Состояние живёт в `SceneSession.Data`, а `SceneContext` уже даёт JSON-backed helper-методы поверх него. -Предлагаемый helper API: +Текущий helper API: -- `ctx.BindState(&dst)` -- `ctx.SaveState(src)` -- `ctx.ClearState()` +- `ctx.BindData(&dst)` +- `ctx.SaveData(src)` Пример: @@ -200,58 +200,54 @@ type ProfileDraft struct { func askName(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) { var draft ProfileDraft - _ = ctx.BindState(&draft) + _ = ctx.BindData(&draft) draft.Name = ctx.Text - if err := ctx.SaveState(draft); err != nil { - return nil, err + if err := ctx.SaveData(draft); err != nil { + return laniakea.SceneResult{}, err } return ctx.Next("age"), nil } ``` -Использование `Data []byte` в `SceneSession` делает контракт `SessionStore` маленьким и не завязанным на конкретный формат хранения. В первой реализации scene helpers могут работать через JSON. +Использование `Data []byte` в `SceneSession` делает контракт `SessionStore` маленьким и не завязанным на конкретный формат хранения. Текущие helper-методы используют JSON. ## Алгоритм маршрутизации -Роутер должен вести себя так: +Текущий роутер ведёт себя так: 1. Построить `MsgContext` для update. 2. Вычислить session key на основе scope сцены и текущего update. 3. Спросить `SessionStore`, есть ли активная сцена. 4. Если активной сцены нет, продолжить обычную маршрутизацию command, payload и update handlers. -5. Если сцена активна, сначала попробовать scene-local command или payload routing. -6. Если локальный route не совпал, вызвать message handler или step handler сцены. +5. Если сцена активна, сначала попробовать scene-local command routing. +6. Если локальный route не совпал, вызвать текущий step handler, затем scene message handler. 7. Если сцена вернула `Stay`, сохранить текущую сессию без изменений. 8. Если сцена вернула `Next(step)`, сохранить новый step и оставить `Data`. 9. Если сцена вернула `Exit`, удалить сессию. 10. Если сцена вернула `Pass`, продолжить обычную маршрутизацию. -## Важные решения по совместимости +## Что уже реализовано -- Вход в сцену должен происходить через явный API вроде `ctx.EnterScene(...)`. -- Выход из сцены тоже должен быть явным. -- Middleware должны продолжать работать на базовом `MsgContext`. -- Scene handlers должны так же пользоваться `ctx.BindArgs(...)` и `ctx.Context()`. -- Первая версия может поставляться с in-memory store плюс интерфейсом `SessionStore` для кастомного persistence. -- Helpers для сериализации состояния должны входить в первый scene API, а не оставаться приложенческим boilerplate. +- `Plugin.NewScene(...)` и `Plugin.AddScene(...)`. +- `Scene.SetScope(...)`, `SetEntry(...)`, `OnStep(...)`, `OnCommand(...)` и `OnMessage(...)`. +- `MsgContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene(...)`. +- `SceneContext.Stay()`, `Next(...)`, `Exit()`, `Pass()`, `BindData(...)` и `SaveData(...)`. +- `SessionStore` плюс дефолтный `MemorySessionStore`. +- Маршрутизация активной сцены до обычного command flow. -## Открытые вопросы +## Что ещё не реализовано или намеренно отложено -- Нужно ли добавлять scene-local payloads уже в первой версии или отложить на вторую? -- Стоит ли поставлять `MemorySessionStore` по умолчанию, или сцены должны требовать явный store? -- Должны ли modal scenes иметь явный fallback mode вроде "pass to normal routing" против "consume silently"? +- Scene-local payload routing пока не реализован. +- Scene runtime намеренно остаётся внутренним; публичный API не отдаёт низкоуровневые helper-методы для lookup sessions. +- Поверхность helper-методов для scene state пока намеренно небольшая. -## Рекомендуемый первый срез реализации +## Оставшаяся работа -- Добавить `Scene`, `SceneContext`, `SceneSession` и `SessionStore`. -- Добавить in-memory session store по умолчанию. -- Добавить `Plugin.NewScene(...)`. -- Добавить `MsgContext.EnterScene(...)`, `ExitScene(...)` и `CurrentScene(...)`. -- Маршрутизировать активные сцены раньше обычного command/payload flow. -- Поддержать scene-local commands и `OnMessage(...)`. -- Поддержать helpers для состояния сцены поверх `SceneSession.Data`. +- Расширить тесты вокруг scene-state helpers и fallback-поведения. +- Решить, нужны ли scene-local payloads уже в первом стабильном scene release. +- Решить, нужен ли вообще дополнительный публичный inspection API. Связанные страницы: diff --git a/Scenes.md b/Scenes.md index a0701df..2e841c0 100644 --- a/Scenes.md +++ b/Scenes.md @@ -1,8 +1,8 @@ # Scenes -DRAFT: this page describes the proposed scene model and is not implemented yet. +Scenes are Laniakea's work-in-progress stateful routing layer for long-lived interactions. The core skeleton already exists in the library: scenes can be registered in plugins, entered through `MsgContext`, persisted through `SessionStore`, and routed before normal command handling. This page describes the current API, the behaviors that already work, and the parts that are still intentionally unfinished. -Scenes are the planned stateful routing layer for long-lived interactions in Laniakea. They are meant to sit above command and payload routing: if a user or chat is inside an active scene, the scene gets the update first and decides whether to handle it, continue, exit, or pass control back to the normal plugin flow. +Scenes sit above command and payload routing: if a user or chat is inside an active scene, the scene gets the update first and decides whether to handle it, continue, exit, or pass control back to the normal plugin flow. ## Why scenes exist @@ -35,7 +35,7 @@ Scenes are the planned stateful routing layer for long-lived interactions in Lan Scene sessions should not assume that "one user" is always the right unit. Telegram private chats and group chats need different defaults. -Proposed scopes: +Current scopes: - `SceneScopeUser`: one session per user across all chats. - `SceneScopeChat`: one session per chat. @@ -47,7 +47,7 @@ Recommended default: - Reserve `SceneScopeUser` for rare account-level flows that intentionally cross chats. - Use `SceneScopeChat` only for room-level shared workflows. -## Proposed types +## Current types ```go type SceneScope int @@ -65,25 +65,27 @@ type SceneSession struct { } type SessionStore interface { - Get(key string) (*SceneSession, error) + Get(key string) (SceneSession, error) Set(key string, session SceneSession) error Delete(key string) error } ``` -## Proposed scene registration +Laniakea currently ships with `MemorySessionStore` as the default implementation, and `Bot.SetSessionStore(...)` can replace it with a custom store. -Scenes should be registered inside plugins in the same style as commands and payloads. +## Current scene registration + +Scenes are registered inside plugins in the same style as commands and payloads. ```go plugin.NewScene("rp"). SetScope(laniakea.SceneScopeUserChat). SetEntry("chat"). OnMessage(handleRPMessage). - AddCommand("rpstop", stopRP) + OnCommand("rpstop", stopRP) ``` -`SetEntry(...)` defines the initial step or state of the scene. When `ctx.EnterScene("rp")` creates a new session, the framework writes that entry step into `SceneSession.Step`. +`SetEntry(...)` defines the initial step or state of the scene. `ctx.EnterScene("rp")` validates that the entry step is set and registered before creating a session. This keeps the plugin API visually consistent: @@ -92,9 +94,9 @@ This keeps the plugin API visually consistent: - `AddUpdateHandler(...)` - `NewScene(...)` -## Scene modes +## Scene shapes -The first version should support two common shapes. +The current skeleton already supports two common shapes. - Step-based scenes: a named step handles each update and decides the next step. - Modal scenes: a long-lived "mode" handles ordinary messages until an explicit exit command ends it. @@ -106,8 +108,8 @@ Step-based flow example: plugin.NewScene("profile"). SetScope(laniakea.SceneScopeUserChat). SetEntry("name"). - AddStep("name", askName). - AddStep("confirm", confirmProfile) + OnStep("name", askName). + OnStep("confirm", confirmProfile) ``` Modal flow example: @@ -117,36 +119,35 @@ plugin.NewScene("rp"). SetScope(laniakea.SceneScopeUserChat). SetEntry("chat"). OnMessage(handleRPMessage). - AddCommand("rpstop", stopRP) + OnCommand("rpstop", stopRP) ``` -## Proposed contexts and results +## Current contexts and results -Normal commands should still use `*MsgContext`. Scene handlers should use a scene-specific wrapper that embeds the normal context and adds state information. +Normal commands still use `*MsgContext`. Scene handlers use `*SceneContext`, which embeds `*MsgContext` and adds scene-state helpers. ```go type SceneContext struct { *MsgContext - Scene string - Step string + // internal session state } -type SceneResult interface{ isSceneResult() } - -type SceneStay struct{} -type SceneNext struct{ Step string } -type SceneExit struct{} -type ScenePass struct{} +type SceneResult struct { + Action SceneAction + Next string +} ``` -Possible helper methods on `SceneContext`: +Current helper methods on `SceneContext`: - `ctx.Stay()` - `ctx.Next(step)` - `ctx.Exit()` - `ctx.Pass()` +- `ctx.BindData(&dst)` +- `ctx.SaveData(src)` -## Proposed handler shapes +## Handler shapes Normal command entry: @@ -182,13 +183,12 @@ func stopRP(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) { Scenes need more than just `Scene` and `Step`. Real flows also accumulate data between updates, such as a draft profile, selected options, or temporary IDs. -That state should not be pushed entirely onto the application author. The framework should provide a storage slot inside `SceneSession` plus ergonomic helpers on `SceneContext`. +That state now lives in `SceneSession.Data`, and `SceneContext` already exposes JSON-backed helpers for it. -Proposed helper API: +Current helper API: -- `ctx.BindState(&dst)` -- `ctx.SaveState(src)` -- `ctx.ClearState()` +- `ctx.BindData(&dst)` +- `ctx.SaveData(src)` Example: @@ -200,58 +200,54 @@ type ProfileDraft struct { func askName(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) { var draft ProfileDraft - _ = ctx.BindState(&draft) + _ = ctx.BindData(&draft) draft.Name = ctx.Text - if err := ctx.SaveState(draft); err != nil { - return nil, err + if err := ctx.SaveData(draft); err != nil { + return laniakea.SceneResult{}, err } return ctx.Next("age"), nil } ``` -Using `Data []byte` in `SceneSession` keeps the `SessionStore` contract small and storage-agnostic. The scene helpers can use JSON for the first implementation. +Using `Data []byte` in `SceneSession` keeps the `SessionStore` contract small and storage-agnostic. The current helpers use JSON. ## Routing algorithm -The router should behave like this: +The current router behaves like this: 1. Build `MsgContext` for the update. 2. Compute the session key from scene scope and the current update. 3. Ask `SessionStore` whether an active scene exists. 4. If no scene is active, continue normal command, payload, and update routing. -5. If a scene is active, try scene-local command or payload routing first. -6. If no scene-local route matches, try the scene message or step handler. +5. If a scene is active, try scene-local command routing first. +6. If no scene-local route matches, try the current step handler, then the scene message handler. 7. If the scene returns `Stay`, keep the current session. 8. If the scene returns `Next(step)`, persist the new step and keep `Data`. 9. If the scene returns `Exit`, delete the session. 10. If the scene returns `Pass`, continue normal routing. -## Important compatibility decisions +## Implemented today -- Scene entry should happen through explicit APIs such as `ctx.EnterScene(...)`. -- Scene exit should also be explicit. -- Middleware should still run on the base `MsgContext`. -- Scene handlers should continue to benefit from `ctx.BindArgs(...)` and `ctx.Context()`. -- The first version can ship with an in-memory store plus a `SessionStore` interface for custom persistence. -- State serialization helpers should be part of the first scene API, not left as app-specific boilerplate. +- `Plugin.NewScene(...)` and `Plugin.AddScene(...)`. +- `Scene.SetScope(...)`, `SetEntry(...)`, `OnStep(...)`, `OnCommand(...)`, and `OnMessage(...)`. +- `MsgContext.EnterScene(...)`, `EnterSceneStep(...)`, and `ExitScene(...)`. +- `SceneContext.Stay()`, `Next(...)`, `Exit()`, `Pass()`, `BindData(...)`, and `SaveData(...)`. +- `SessionStore` plus the default `MemorySessionStore`. +- Active-scene routing before normal command flow. -## Open questions +## Still missing or intentionally deferred -- Should scene-local payloads be added in the first version or the second? -- Should the framework ship with a default `MemorySessionStore`, or should scenes require an explicit store? -- Should modal scenes have an explicit fallback mode such as "pass to normal routing" versus "consume silently"? +- Scene-local payload routing is not implemented yet. +- The scene runtime remains intentionally internal; the public API does not expose low-level session lookup helpers. +- The scene-state helper surface is still intentionally small. -## Recommended first implementation slice +## Remaining work -- Add `Scene`, `SceneContext`, `SceneSession`, and `SessionStore`. -- Add a default in-memory session store. -- Add `Plugin.NewScene(...)`. -- Add `MsgContext.EnterScene(...)`, `ExitScene(...)`, and `CurrentScene(...)`. -- Route active scenes before normal command and payload flow. -- Support scene-local commands and `OnMessage(...)`. -- Support scene-state helpers backed by `SceneSession.Data`. +- Expand tests around scene-state helpers and fallback behavior. +- Decide whether scene-local payloads belong in the first stable scene release. +- Decide whether any additional public inspection API is actually needed. Related pages: