diff --git a/SVSim.BattleNode/Lifecycle/ScriptedLifecycle.cs b/SVSim.BattleNode/Lifecycle/ScriptedLifecycle.cs
new file mode 100644
index 0000000..47ec22c
--- /dev/null
+++ b/SVSim.BattleNode/Lifecycle/ScriptedLifecycle.cs
@@ -0,0 +1,162 @@
+using SVSim.BattleNode.Protocol;
+
+namespace SVSim.BattleNode.Lifecycle;
+
+///
+/// v1 Path-A scripted opponent. Hand-rolled static frames good enough to land the client on
+/// the mulligan screen and let them play turn 1. Templates derived from
+/// data_dumps/captures/battle-traffic_tk2_regular.ndjson.
+///
+public static class ScriptedLifecycle
+{
+ /// 30 dummy cardIds — repeats of a stable neutral card.
+ public static readonly long DummyCardId = 100011010;
+
+ public const long FakeOpponentViewerId = 999_999_999L;
+
+ public static MsgEnvelope BuildMatched(long playerViewerId, long opponentViewerId, string battleId)
+ {
+ var body = new Dictionary
+ {
+ ["selfInfo"] = new Dictionary
+ {
+ ["country_code"] = "KOR",
+ ["userName"] = "Player",
+ ["sleeveId"] = "3000011",
+ ["emblemId"] = "701441011",
+ ["degreeId"] = "300003",
+ ["fieldId"] = 43,
+ ["isOfficial"] = 0,
+ ["oppoId"] = opponentViewerId,
+ ["seed"] = 17548138L,
+ },
+ ["oppoInfo"] = new Dictionary
+ {
+ ["country_code"] = "JPN",
+ ["userName"] = "Opponent",
+ ["sleeveId"] = "704141010",
+ ["emblemId"] = "400001100",
+ ["degreeId"] = "120027",
+ ["fieldId"] = 5,
+ ["isOfficial"] = 0,
+ ["oppoId"] = playerViewerId,
+ ["seed"] = 17548138L,
+ ["oppoDeckCount"] = 30,
+ },
+ ["selfDeck"] = BuildDummyDeck(),
+ };
+ return EnvelopeForPush(NetworkBattleUri.Matched, body, bid: battleId);
+ }
+
+ public static MsgEnvelope BuildBattleStart(long playerViewerId)
+ {
+ var body = new Dictionary
+ {
+ ["turnState"] = 0, // player goes first
+ ["battleType"] = 11, // TK2 NetworkBattleType
+ ["selfInfo"] = new Dictionary
+ {
+ ["rank"] = "10",
+ ["battlePoint"] = "6270",
+ ["classId"] = "1",
+ ["charaId"] = "1",
+ ["cardMasterName"] = "card_master_node_10015",
+ },
+ ["oppoInfo"] = new Dictionary
+ {
+ ["rank"] = "1",
+ ["isMasterRank"] = "0",
+ ["battlePoint"] = 0,
+ ["masterPoint"] = "0",
+ ["classId"] = "8",
+ ["charaId"] = "8",
+ ["cardMasterName"] = "card_master_node_10015",
+ },
+ };
+ return EnvelopeForPush(NetworkBattleUri.BattleStart, body);
+ }
+
+ public static MsgEnvelope BuildDeal()
+ {
+ var body = new Dictionary
+ {
+ ["self"] = new List