diff --git a/SVSim.Database/Repositories/Card/ICardInventoryRepository.cs b/SVSim.Database/Repositories/Card/ICardInventoryRepository.cs
index 4dc66f2..90bb475 100644
--- a/SVSim.Database/Repositories/Card/ICardInventoryRepository.cs
+++ b/SVSim.Database/Repositories/Card/ICardInventoryRepository.cs
@@ -18,6 +18,14 @@ public interface ICardInventoryRepository
/// when validation fails. On error nothing is written.
///
Task DestructCards(long viewerId, IReadOnlyDictionary destructCounts);
+
+ ///
+ /// Validate-then-mutate craft of cards from RedEther. Atomic in a transaction; on validation
+ /// failure nothing is written. Routes Card grants through
+ /// so the CardCosmeticReward cascade fires for first-time owners.
+ ///
+ /// cardId → num_to_create. Empty dict is rejected by the caller.
+ Task CreateCards(long viewerId, IReadOnlyDictionary createCounts);
}
///
@@ -42,3 +50,28 @@ public enum DestructError
CardProtected,
InsufficientCards,
}
+
+public sealed record CreateOutcome(CreateResult? Result, CreateError? Error)
+{
+ public bool IsSuccess => Result is not null;
+
+ public static CreateOutcome Ok(CreateResult r) => new(r, null);
+ public static CreateOutcome Fail(CreateError e) => new(null, e);
+}
+
+///
+/// Outcome of a successful create. is the flattened
+/// list returned by
+/// — one Card entry per crafted cardId plus any cosmetic-cascade entries.
+///
+public sealed record CreateResult(
+ ulong NewRedEtherTotal,
+ IReadOnlyList Grants);
+
+public enum CreateError
+{
+ UnknownCard,
+ NotCraftable,
+ WouldExceedMaxCopies,
+ InsufficientVials,
+}