diff --git a/SVSim.BattleNode/Sessions/IBattleParticipant.cs b/SVSim.BattleNode/Sessions/IBattleParticipant.cs
new file mode 100644
index 0000000..a129f56
--- /dev/null
+++ b/SVSim.BattleNode/Sessions/IBattleParticipant.cs
@@ -0,0 +1,46 @@
+using SVSim.BattleNode.Bridge;
+using SVSim.BattleNode.Protocol;
+
+namespace SVSim.BattleNode.Sessions;
+
+///
+/// One side of a battle. Two of these are held by a BattleSession; the session
+/// brokers between them. Concrete impls (added in subsequent Phase-1 tasks):
+///
+/// - RealParticipant — WS-backed.
+/// - NoOpBotParticipant — silent; for BattleType.Bot (AI-passive).
+/// - ScriptedBotParticipant — wraps the v1.2 lifecycle for
+/// BattleType.Scripted (solo testing harness).
+///
+///
+public interface IBattleParticipant : IAsyncDisposable
+{
+ /// Real viewer id, or a synthetic stable id for bots
+ /// ().
+ long ViewerId { get; }
+
+ /// Per-battle MatchContext snapshot, used for building Matched/BattleStart
+ /// selfInfo when this participant is "self" in the perspective.
+ MatchContext Context { get; }
+
+ /// Session calls this to deliver a frame from the OTHER participant
+ /// (or a server-synthesized broadcast). Real impl: encode + WS-send.
+ /// NoOp: swallow. Scripted: may emit a response via .
+ /// True for control frames (BattleFinish, JudgeResult, ack);
+ /// bypasses playSeq assignment + archive.
+ Task PushAsync(MsgEnvelope envelope, bool noStock, CancellationToken ct);
+
+ /// Participant fires this when it has a frame to send TO the session
+ /// (its own gameplay action). Real impl: fires on WS recv. NoOp: never fires.
+ /// Scripted: fires from inside PushAsync when the scripted lifecycle wants to
+ /// respond to an inbound frame.
+ event Func? FrameEmitted;
+
+ /// Drives the participant's inbound loop. For Real: the WS read loop
+ /// (returns when the WS closes). For NoOp/Scripted: completes immediately (the
+ /// session keeps running as long as the OTHER participant's RunAsync is alive).
+ Task RunAsync(CancellationToken ct);
+
+ /// Called when the battle ends. Concrete impls clean up (close WS, etc.).
+ Task TerminateAsync(BattleFinishReason reason);
+}