Skip to content

core: seal PlayObservation seats — opaque Position tokens instead of live BasePlayer references #6

Description

@valmathieu

Problem

PlayObservation carries live BasePlayer references inside its Play entries (completed_tricks / current_trick), so a card-play strategy reaching through play.player.hand can technically still see another seat's cards. The projection seals what is handed over, not every object path reachable from it. This is the "Observation seat-sealing" follow-up already noted in the trust-boundary comment in packages/contrai-core/src/contrai_core/play.py (around the Play/PlayObservation definitions).

Proposal

Reuse the planned Position enum as the opaque seat token: observation-facing Play records carry Position instead of the live player. That is exactly the public information a real player has at the table ("West played the jack") — nothing more.

  • Depends on: Position enum, and preferably TeamSide (strategies currently compare play.player.team is self.team; they would work from Position.team_side instead).
  • Engine strategies (model/player/rule_based/card_play.py void tracking, partner-master checks, opponents-still-to-play logic) migrate from player references to seat tokens.

Why (roadmap)

Prerequisite for honest supervised/RL training: if observations leak hidden state, a learned policy will find and exploit the leak, invalidating evaluation results. Should land before any learning work consumes observations.

Acceptance criteria

  • No object path from a PlayObservation reaches another seat's Hand.
  • Rule-based AI behaviour unchanged (existing expert-AI scenario tests stay green).
  • Unit tests assert observation contents expose seats only as Position.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions