Skip to content

feat: add per-turn state foundation (state scopes, container, loader) - #553

Open
Lily Du (lilyydu) wants to merge 6 commits into
mainfrom
lilyydu/01-state-foundation
Open

feat: add per-turn state foundation (state scopes, container, loader)#553
Lily Du (lilyydu) wants to merge 6 commits into
mainfrom
lilyydu/01-state-foundation

Conversation

@lilyydu

@lilyydu Lily Du (lilyydu) commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

This is PR 1 of 5 in the multi-connection OAuth stack. It lands the per-turn state layer on its own so the rest of the stack can build on it. It is purely additive — no existing files are modified and nothing is wired into dispatch yet (that's PR 2). The design mirrors the C# Microsoft.Teams.Apps.State package so the SDKs stay behaviorally aligned.

New subpackage microsoft_teams.apps.state (not yet re-exported from the public apps package — deliberate; PR 2 wires ctx.state / App(state=...)):

Decomposition

  • TurnState — a MutableMapping[str, Any] scope with dirty tracking (loader only writes mutated scopes) and sealing (post-turn access raises TurnStateSealedError).

  • TurnStateContainer — bundles the conversation scope and optional user scope plus their identity; exposes seal() / delete().

  • TurnStateLoader — load/save/delete over the Storage ABC. Scopes persist as JSON strings ({"ts": <epoch>, "data": {...}}); empty-but-dirty scopes delete their key, clean scopes are skipped.

  • StateOptionsstorage, key_prefix (default "ts"), ttl.

Parity with C#

  • Key layout matches exactly: {prefix}:conv:{conversationId} / {prefix}:user:{conversationId}:{userId}, default prefix "ts".

  • Same TurnState/TurnStateContainer split, dirty/seal semantics, empty-scope-deletes-key behavior.

  • Intentional divergence: C# delegates expiry to IDistributedCache; our Storage ABC has no native TTL, so blobs carry a ts and the loader enforces ttl on load. No schema-version field (C# has none either).

  • Values must be JSON-serializable (non-serializable raises TypeError at save; documented on TurnState).

Design Choices

  • I subclassed MutableMapping so TurnState feels like a normal dict to users while funneling every mutation through a single point, giving us dirty-tracking and sealing for free without overriding each method (which subclassing dict would've required).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TurnStateContainer and TurnStateLoader (+ StateOptions) for load/save/delete over Storage.
  • 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.

Comment thread packages/apps/src/microsoft_teams/apps/state/loader.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/loader.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/container.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/turn_state.py
Comment thread packages/apps/src/microsoft_teams/apps/state/container.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/turn_state.py
Comment thread packages/apps/src/microsoft_teams/apps/state/container.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/loader.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/loader.py
Comment thread packages/apps/src/microsoft_teams/apps/state/loader.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/options.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/state/turn_state.py
Comment thread packages/apps/src/microsoft_teams/apps/state/options.py Outdated
Lily Du (lilyydu) and others added 4 commits August 13, 2026 14:23
…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>
@lilyydu
Lily Du (lilyydu) force-pushed the lilyydu/01-state-foundation branch from bc16aa4 to 85ec356 Compare August 13, 2026 21:23
Comment thread packages/apps/src/microsoft_teams/apps/state/loader.py
Comment thread packages/apps/src/microsoft_teams/apps/state/turn_state.py
Comment thread packages/apps/src/microsoft_teams/apps/state/loader.py Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants