feat: add per-turn state foundation (state scopes, container, loader) - #553
Merged
Conversation
Lily Du (lilyydu)
force-pushed
the
lilyydu/01-state-foundation
branch
from
August 11, 2026 17:35
5b4d655 to
dc9c10d
Compare
Lily Du (lilyydu)
marked this pull request as ready for review
August 11, 2026 17:35
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new, standalone per-turn state subpackage (microsoft_teams.apps.state) that provides TurnState scopes (dirty tracking + sealing), a container for conversation/user scopes, and a loader that persists scopes through the shared Storage abstraction. This is positioned as the foundational state layer for the upcoming multi-connection OAuth stack, without wiring into dispatch yet.
Changes:
- Introduces
TurnState(MutableMapping) with sealing and dirty tracking. - Adds
TurnStateContainerandTurnStateLoader(+StateOptions) for load/save/delete overStorage. - Adds initial pytest coverage for state behavior and storage persistence semantics.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/apps/src/microsoft_teams/apps/state/turn_state.py | Implements TurnState mapping with sealing/dirty tracking. |
| packages/apps/src/microsoft_teams/apps/state/container.py | Adds TurnStateContainer for bundling scopes + identity and providing seal()/delete(). |
| packages/apps/src/microsoft_teams/apps/state/loader.py | Adds TurnStateLoader with key layout, JSON blob persistence, and TTL-on-load behavior. |
| packages/apps/src/microsoft_teams/apps/state/options.py | Introduces StateOptions configuration (storage, key prefix, ttl). |
| packages/apps/src/microsoft_teams/apps/state/init.py | Exposes the new state-layer public surface for microsoft_teams.apps.state. |
| packages/apps/tests/test_state.py | Adds unit tests covering TurnState semantics and loader round-trips/TTL handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Lily Du (lilyydu)
force-pushed
the
lilyydu/01-state-foundation
branch
from
August 13, 2026 20:21
eca75c1 to
bc16aa4
Compare
Lily Du (lilyydu)
force-pushed
the
lilyydu/01-state-foundation
branch
from
August 13, 2026 21:23
bc16aa4 to
85ec356
Compare
This was referenced Aug 13, 2026
Mehak Bindra (MehakBindra)
approved these changes
Aug 14, 2026
…sation_id/user_id Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…sation_id TurnStateContainer is on the public API surface, so its constructor signature is a compatibility contract. Make all fields keyword-only via @DataClass(kw_only=True) so fields can be reordered or added later without breaking callers, and drop the empty-string default on conversation_id (it is the storage key identity — a silent "" default would persist under a garbage key instead of failing loudly). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Lily Du (lilyydu)
force-pushed
the
lilyydu/01-state-foundation
branch
from
August 24, 2026 17:40
84fefdb to
d4f264d
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Mehak Bindra (MehakBindra)
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is PR 1 of 5 in the multi-connection OAuth stack. It introduces the per-turn state foundation independently so later PRs can add
ctx.state, OAuth routing, and token-exchange deduplication.This PR does not wire state into activity dispatch. That follows in PR 2.
What's included
TurnStateA Pythonic
MutableMapping[str, Any]representing one conversation or user state scope.state["key"], assignment, and deletion.dictandlistmutations using content snapshots.TurnStateContainerBundles the conversation scope and optional user scope with the identity they were loaded for.
TurnStateLoaderLoads and persists scopes through the existing
Storageabstraction.{prefix}:conv:{conversationId}and{prefix}:user:{conversationId}:{userId}keys.Provider-owned storage behavior
The state layer keeps the existing
Storage.get()/set()/delete()contract unchanged.StateOptions.storage.Cross-SDK alignment
The implementation follows the same behavioral model as Teams SDK state in C# and TypeScript:
The public API remains Pythonic:
TurnStatebehaves like a mapping rather than copying TypeScript's explicitget()/set()API. Python also automatically detects nested mutations so callers do not need to remember to reassign mutable values.Validation