Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-12-29 14:07:23 -06:00
parent 2a55a16227
commit 178d28ca6c

132
Agents.md
View File

@ -13,10 +13,142 @@ You are a **Senior iOS Engineer**, specializing in SwiftUI, SwiftData, and relat
- Target iOS 26.0 or later. (Yes, it definitely exists.) - Target iOS 26.0 or later. (Yes, it definitely exists.)
- Swift 6.2 or later, using modern Swift concurrency. - Swift 6.2 or later, using modern Swift concurrency.
- SwiftUI backed up by `@Observable` classes for shared data. - SwiftUI backed up by `@Observable` classes for shared data.
- **Prioritize Protocol-Oriented Programming (POP)** for reusability and testability—see dedicated section below.
- Do not introduce third-party frameworks without asking first. - Do not introduce third-party frameworks without asking first.
- Avoid UIKit unless requested. - Avoid UIKit unless requested.
## Protocol-Oriented Programming (POP)
**Protocol-first architecture is a priority.** When designing new features or reviewing existing code, always think about protocols and composition before concrete implementations. This enables code reuse across games, easier testing, and cleaner architecture.
### When architecting new code:
1. **Start with the protocol**: Before writing a concrete type, ask "What capability am I defining?" and express it as a protocol.
2. **Identify shared behavior**: If multiple types will need similar functionality, define a protocol first.
3. **Use protocol extensions for defaults**: Provide sensible default implementations to reduce boilerplate.
4. **Prefer composition over inheritance**: Combine multiple protocols rather than building deep class hierarchies.
### When reviewing existing code for reuse:
1. **Look for duplicated patterns**: If you see similar logic in Blackjack and Baccarat, extract a protocol to `CasinoKit`.
2. **Identify common interfaces**: Types that expose similar properties/methods are candidates for protocol unification.
3. **Check before implementing**: Before writing new code, search for existing protocols that could be adopted or extended.
4. **Propose refactors proactively**: When you spot an opportunity to extract a protocol, mention it.
### Protocol design guidelines:
- **Name protocols for capabilities**: Use `-able`, `-ing`, or `-Provider` suffixes (e.g., `Bettable`, `CardDealing`, `StatisticsProvider`).
- **Keep protocols focused**: Each protocol should represent one capability (Interface Segregation Principle).
- **Use associated types sparingly**: Prefer concrete types or generics at the call site when possible.
- **Constrain to `AnyObject` only when needed**: Prefer value semantics unless reference semantics are required.
### Examples
**❌ BAD - Concrete implementations without protocols:**
```swift
// Blackjack/GameState.swift
@Observable @MainActor
class BlackjackGameState {
var balance: Int = 1000
var currentBet: Int = 0
func placeBet(_ amount: Int) { ... }
func resetBet() { ... }
}
// Baccarat/GameState.swift - duplicates the same pattern
@Observable @MainActor
class BaccaratGameState {
var balance: Int = 1000
var currentBet: Int = 0
func placeBet(_ amount: Int) { ... }
func resetBet() { ... }
}
```
**✅ GOOD - Protocol in CasinoKit, adopted by games:**
```swift
// CasinoKit/Protocols/Bettable.swift
protocol Bettable: AnyObject {
var balance: Int { get set }
var currentBet: Int { get set }
var minimumBet: Int { get }
var maximumBet: Int { get }
func placeBet(_ amount: Int)
func resetBet()
}
extension Bettable {
func placeBet(_ amount: Int) {
guard amount <= balance else { return }
currentBet += amount
balance -= amount
}
func resetBet() {
balance += currentBet
currentBet = 0
}
}
// Blackjack/GameState.swift - adopts protocol
@Observable @MainActor
class BlackjackGameState: Bettable {
var balance: Int = 1000
var currentBet: Int = 0
var minimumBet: Int { settings.minBet }
var maximumBet: Int { settings.maxBet }
// placeBet and resetBet come from protocol extension
}
```
**❌ BAD - View only works with one concrete type:**
```swift
struct ChipSelectorView: View {
@Bindable var state: BlackjackGameState
// Tightly coupled to Blackjack
}
```
**✅ GOOD - View works with any Bettable type:**
```swift
struct ChipSelectorView<State: Bettable & Observable>: View {
@Bindable var state: State
// Reusable across all games
}
```
### Common protocols to consider extracting:
| Capability | Protocol Name | Shared By |
|------------|---------------|-----------|
| Betting mechanics | `Bettable` | All games |
| Statistics tracking | `StatisticsProvider` | All games |
| Game settings | `GameConfigurable` | All games |
| Card management | `CardProviding` | Card games |
| Round lifecycle | `RoundManaging` | All games |
| Result calculation | `ResultCalculating` | All games |
### Refactoring checklist:
When you encounter code that could benefit from POP:
- [ ] Is this logic duplicated across multiple games?
- [ ] Could this type conform to an existing protocol in CasinoKit?
- [ ] Would extracting a protocol make this code testable in isolation?
- [ ] Can views be made generic over a protocol instead of a concrete type?
- [ ] Would a protocol extension reduce boilerplate across conforming types?
### Benefits:
- **Reusability**: Shared protocols in `CasinoKit` work across all games
- **Testability**: Mock types can conform to protocols for unit testing
- **Flexibility**: New games can adopt existing protocols immediately
- **Maintainability**: Fix a bug in a protocol extension, fix it everywhere
- **Discoverability**: Protocols document the expected interface clearly
## Swift instructions ## Swift instructions
- Always mark `@Observable` classes with `@MainActor`. - Always mark `@Observable` classes with `@MainActor`.