aws-samples / aws-samples/sample-ai-possibilities
Opponent possession is incorrectly resolved as own possession when agent IDs are reused across teams
- Dominant language
- Python
- Stars
- 40
- Forks
- 36
- Avg merge
- 5h 26m
- Merged PRs (30d)
- 1
Description
# Bug: Opponent possession incorrectly classified as own-team possession
## Summary
During a match, possession logs never show opponent possession.
The logs only contain:
held by MY player X
held by free
and never:
held by OPP player X
even when the opponent player is clearly controlling the ball.
The issue is caused by an ambiguous possession identity. The ball state provides only `possessionAgentId`, while agent IDs are reused across teams. The current resolver cannot uniquely identify the possession holder and would incorrectly classify opponent possession as own-team possession.
------------------------------------------------------------------------
## Observed Behavior
Example ball state:
``` json
{
"position": {
"x": 0.5445947,
"y": 0.13751486,
"z": 0.108918935
},
"velocity": {
"x": 0,
"y": 0,
"z": 0
},
"isFree": false,
"possessionAgentId": "agentId_4"
}
```
The ball state only contains:
possessionAgentId = agentId_4
However, both teams reuse the same agent IDs:
``` json
[
{
"teamCode": "home",
"agentId": "agentId_4"
},
{
"teamCode": "away",
"agentId": "agentId_4"
}
]
```
`agentId_4` is team-scoped and is not globally unique inside a match.
------------------------------------------------------------------------
## Root Cause
The issue has one root cause and two resulting effects.
## 1. Possession identity is incomplete and cannot be resolved uniquely
The ball state provides only:
possessionAgentId = agentId_4
However, `agentId` is only unique within a team.
A valid player identity requires:
teamCode + agentId
For example:
(home, agentId_4) != (away, agentId_4)
Therefore, `possessionAgentId` alone cannot determine whether the possession belongs to the home team or away team.
------------------------------------------------------------------------
## 2. get_possession_info() resolves ambiguous IDs incorrectly
The current resolver implementation in `lib/state.py` uses:
``` python
holder = next(
(p for p in players if _player_idx(p) == possession_id),
None
)
```
The `_player_idx()` helper converts:
home.agentId_4 -> 4
away.agentId_4 -> 4
Both players therefore have the same lookup key.
Because home players appear first in the player list, `next()` returns the home player whenever both teams contain the same agent index.
Example:
Actual possession:
away.agentId_4
Resolver result:
home.agentId_4
Generated summary:
held by MY player 4
The team check happens only after the player has already been selected, so it cannot correct the mismatch.
The resolver effectively treats a team-scoped `agentId` as globally unique.
------------------------------------------------------------------------
## 3. hasBall is incorrectly determined using the same ambiguous identity
The same identity issue affects the `hasBall` field generated for the current agent.
The current implementation uses:
``` python
has_ball = possession_id == my_player_id
```
This comparison ignores team identity.
Example:
My player:
home.agentId_4
Actual possession:
away.agentId_4
Both are converted to:
possession_id = 4
my_player_id = 4
Therefore:
``` python
has_ball = True
```
even though the opponent actually has possession.
This can generate misleading state information:
Ball: held by MY player 4
YOUR PLAYER (... id=4):
hasBall=True
when the actual possession belongs to:
away.agentId_4
### Note on local workaround limitations
Because the ball state does not include the possession holder's team identity, downstream code cannot reliably resolve the possession owner from `possessionAgentId` alone.
A reliable fix requires either:
- adding team information to the possession data, or
- providing a globally unique player identifier.
------------------------------------------------------------------------
## Steps to Reproduce
1. Start a match with both home and away teams.
2. Enable game state / after summarize_state logging.
3. Wait until an away player gains possession.
4. Check the generated possession summary in CloudWatch logs.
------------------------------------------------------------------------
## Expected Result
When an opponent player has possession:
held by OPP player X
The current agent should also receive:
hasBall=False
------------------------------------------------------------------------
## Actual Result
The state can incorrectly report:
held by MY player X
and:
hasBall=True
when the opponent has possession.
------------------------------------------------------------------------
## Expected Behavior
The possession system should correctly distinguish between players with the same agent ID on different teams.
Situation Expected output
------------------------------ ------------------------
Own player has the ball `held by MY player X`
Opponent player has the ball `held by OPP player X`
No player controls the ball `held by free`
The ball possession holder should include team information so that it can be uniquely identified using:
teamCode + agentId
------------------------------------------------------------------------
## Impact
This issue affects the entire multi-agent decision system because possession information is one of the key factors used by agents to make tactical decisions and is included in the shared game state provided to all agents.
Since each agent uses the summarized game state as LLM context for decision-making, incorrect possession information can cause agents to reason from an incorrect understanding of the match situation.
Potential impact:
- All LLM agents may receive incorrect possession context.
- Offensive and defensive roles may make decisions based on an incorrect game state.
- Defending agents may fail to recognize opponent possession and react too late.
- Attacking agents may incorrectly assume possession and generate suboptimal tactical actions.
- Multi-agent coordination may degrade because agents share an inconsistent view of the game state.
- Match analysis, possession statistics, and historical training data may become unreliable.
Contributor guide
Research direction
Start in lib/state.py at get_possession_info(), _player_idx(), and the hasBall comparison, then trace where possessionAgentId is produced and shaped. Confirm how team identity or a globally unique player identifier can reach the resolver; done means own and opponent players with reused IDs produce distinct possession summaries and opponent possession yields hasBall=False.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100