From 1ff3ccc4ebe92c1c61de2ccaddd6da6a161ee34d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 8 Mar 2026 21:43:45 +0000 Subject: [PATCH] Add CLAUDE.md with codebase documentation for AI assistants Documents project structure, build/test commands, Swift conventions, core calculation formulas, unit handling patterns, and CI setup. https://claude.ai/code/session_018GoXszdZBuW6A4w1Y5KA6A --- CLAUDE.md | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3abe241 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,160 @@ +# CLAUDE.md — KiteboardingKit + +This file documents the codebase structure, conventions, and workflows for AI assistants working in this repository. + +## Project Overview + +**KiteboardingKit** is a zero-dependency Swift library that provides kiteboarding/kitesurfing equipment recommendation logic for Apple platforms. The formulas originate from [Jim Douglass's interactive kiteboarding calculator spreadsheet](http://jimbodouglass.blogspot.com/2011/01/interactive-kiteboarding-calculator.html). + +**Language**: Swift 5.9+ +**Package Manager**: Swift Package Manager (SPM) +**Type**: Pure library — no app target, no external dependencies +**License**: MIT + +--- + +## Repository Structure + +``` +KiteboardingKit/ +├── .github/workflows/swift.yml # CI: build + test on macOS with Swift 5.9 +├── Sources/KiteboardingKit/ +│ ├── KiteboardingKit.swift # Main protocol + calculator implementation (203 lines) +│ ├── KiteboardingKitError.swift # Error enum (2 cases) +│ ├── KiteSize.swift # KiteSize typealias + KiteSizeRange struct +│ ├── Wind.swift # WindSpeed struct (ideal/min/max) +│ ├── BoardSize.swift # BoardSize + BoardOptions structs +│ └── Measurements+Extensions.swift # Convenience extensions on Measurement +├── Tests/KiteboardingKitTests/ +│ ├── KiteTests.swift # 6 test methods covering kite calculations +│ └── BoardTests.swift # 4 test methods covering board calculations +├── Package.swift # SPM manifest — swift-tools-version: 5.9 +└── README.md +``` + +--- + +## Build & Test Commands + +```bash +# Build the library +swift build -v + +# Run all tests +swift test -v + +# Run a specific test +swift test --filter KiteTests/testKiteSize +``` + +All CI runs on `macos-latest` with Swift 5.9.0 (see `.github/workflows/swift.yml`). + +--- + +## Source Code Architecture + +### Protocol-First Design + +The public API is defined as a protocol: + +```swift +// Sources/KiteboardingKit/KiteboardingKit.swift +public protocol KiteboardingCalculatorType { + func trainerKiteSize(weight:windSpeed:) throws -> KiteSizeRange + func kiteSize(weight:windSpeed:) throws -> KiteSizeRange + func windSpeed(weight:kiteSize:) throws -> WindSpeed + func boardSize(weight:) throws -> BoardOptions +} +``` + +The concrete type is `KiteboardingCalculator` (a struct). Tests use the protocol type to stay implementation-agnostic. + +### Unit Handling Convention + +All public API parameters accept `Measurement` from Apple's Foundation framework — this gives callers type-safe unit conversions (kg, lbs, knots, mph, km/h, etc.). + +Internally, every function converts to SI units before computing: +- Mass → **kilograms** +- Speed → **knots** + +Convenience factory methods live in `Measurements+Extensions.swift`: + +```swift +Measurement.pounds(150) +Measurement.kilograms(68) +Measurement.knots(20) +Measurement.kilometresPerHour(37) +``` + +### Core Formulas + +| Method | Formula | Notes | +|--------|---------|-------| +| `trainerKiteSize` | `(0.52 × kg) / knots` | Throws if wind > 24 kn | +| `kiteSize` (ideal) | `(2.175 × kg) / knots` | — | +| `kiteSize` (min) | `0.75 × ideal` | — | +| `kiteSize` (max) | `1.50 × ideal` | — | +| `windSpeed` (ideal) | `(2.175 × kg) / m²` | Inverse of kiteSize | +| `boardSize` | `modifier × kg^(1/3)` | Different modifier per wind condition | + +Results are rounded to **1 decimal place**. + +### Error Handling + +```swift +// Sources/KiteboardingKit/KiteboardingKitError.swift +public enum KiteboardingKitError: Error { + case difficultToKiteboard // Conditions outside usable range + case trainerKiteAboveSafeLevel // Wind > 24 knots for learner kite +} +``` + +### Return Types + +| Type | Fields | +|------|--------| +| `KiteSizeRange` | `ideal`, `minimum`, `maximum` (all `Double`, m²) | +| `WindSpeed` | `ideal`, `minimum`, `maximum` (all `Measurement`) | +| `BoardSize` | `length` (cm), `width` (cm), `area` (computed, cm²) | +| `BoardOptions` | `beginner`, `lightWind`, `normalWind`, `hardWind` (all `BoardSize`) | + +--- + +## Key Conventions + +- **Structs over classes** — all public types are value types. +- **No external dependencies** — keep the library dependency-free. +- **Foundation `Measurement` for all units** — never pass raw `Double` for a physical quantity in the public API. +- **Internal SI conversion** — convert to kg/knots before any calculation, convert back for output if needed. +- **MARK comments** — use `// MARK: -` to separate logical sections within files. +- **Protocol-based abstraction** — new calculator variants should conform to `KiteboardingCalculatorType`. +- **Rounding** — results rounded to 1 decimal place using `(value * 10).rounded() / 10`. + +--- + +## Testing Conventions + +- Framework: **XCTest** +- Test files mirror the feature they test (`KiteTests.swift` → kite calculations, `BoardTests.swift` → board calculations). +- Tests use both imperial and SI inputs to verify unit conversion correctness (e.g., `testKiteSize` vs. `testKiteSizeSI`). +- Prefer `XCTAssertEqual` with explicit `accuracy:` for floating-point comparisons where relevant. +- All test methods should be `func test()` — no parameters. + +--- + +## CI/CD + +Defined in `.github/workflows/swift.yml`: +- Triggers on **push** and **pull request** to `main`. +- Runs `swift build -v` then `swift test -v` on `macos-latest` with Swift 5.9.0. +- There is no separate lint step — the Swift compiler warnings serve as the linter. + +--- + +## What to Avoid + +- Do **not** add external Swift package dependencies without a strong justification. +- Do **not** expose raw `Double` values for physical quantities in the public API — wrap them in `Measurement`. +- Do **not** introduce classes when a struct will do. +- Do **not** change the rounding behavior without updating the test expectations. +- Do **not** push directly to `main` — use a pull request.