Quick orientation for AI agents working on the Unit codebase.
The authoritative source is docs/AGENTS.md and CLAUDE.md. This root pointer just sits where tooling expects to find it.
Unit is a zero-friction, progression-guided gym logging tool for iOS. The primary program unit is the Template — a lightweight repeatable routine. The core UI paradigm is Last time — the app pre-fills weight and reps from the last session so the user can log a set with a single tap.
The Gym Test applies: logging a set (weight, reps) in under 3 seconds under physical stress. Version 2.1 adds one opt-in, post-workout double-progression suggestion while preserving the active logging UI. The old cycle/failure/deload ProgressionEngine remains removed. See docs/claude/scope.md for the current boundary.
- Swift 6 (concurrency-safe), SwiftUI (NavigationStack), SwiftData (local-first; no CloudKit in v1).
- iOS 18+ (Live Activities for rest timer).
- Swift Charts — no third-party charting.
- Geist / Geist Mono bundled
.ttffonts.
| Topic | Location |
|---|---|
| Session-level intent (read first) | CLAUDE.md |
| Product (persona, voice, principles) | PRODUCT.md |
| Design system (palette, type, components, do/don't) | DESIGN.md + DESIGN.json |
| UX rules + scope fences (full) | docs/AGENTS.md |
| Atomic layers + tokens + banned patterns | docs/atomic-design-system.md |
| Visual language (light-first, hierarchy, Gym Test) | docs/visual-language.md |
| Compass (decisions, positioning, decision log) | docs/product-compass.md |
| v1 ships / does-not-ship + push-back phrasing | docs/claude/scope.md |
| Apple HIG reference | docs/apple-hig.md |
| Visual references library (iOS screenshots) | docs/references/ |
| Folder | Contents |
|---|---|
Unit/Models/ |
SwiftData models: DayTemplate, Exercise, WorkoutSession, SetEntry |
Unit/Features/Today/ |
TodayView, ActiveWorkoutView, TrainingWeekProgress, RestTimerAttributes |
Unit/Features/Templates/ |
TemplatesView, TemplateDetailView, AddTemplateView, ProgramLibrary*View, ProgramDetailView, ExercisesListView |
Unit/Features/History/ |
HistoryView (single list), SessionDetailView, ExerciseProgressView |
Unit/Features/Onboarding/ |
Splash → import method → program-import → split-builder → exercises |
Unit/Features/Settings/ |
SettingsView (weight unit, restart onboarding) |
Unit/Features/Subscription/ |
PaywallView / StoreManager (hard post-onboarding StoreKit gate; weekly, monthly, yearly, and optional lifetime access) |
Unit/Features/ProgramLaunch/ |
Quick-start support |
Unit/UI/ |
DesignSystem.swift — atoms, molecules, organisms, AppScreen template |
Unit/Resources/Fonts/ |
Geist + Geist Mono .ttf |
- Light mode only. No
.preferredColorScheme(.dark), no dark-first decisions. Tokens may carry dark values for system compatibility, but visual review and screenshots happen in light mode. - Portrait only. No landscape support.
- Prefill order: an accepted progression target for the same routine and exercise takes precedence, then the latest valid completed session across templates, then explicit starting values saved with the current template/program, then the truly empty state. Only completed history is labelled Last time; planned values are Starting target. Never display "0 kg" — bodyweight shows "BW".
- Templates are the program unit. Not cycles, not weeks, not engines.
- Adaptive appearance via tokens only. Use
AppColor/AppFont/AppSpacing/AppRadius/AppIconfromUnit/UI/DesignSystem.swift. No rawColor(...), hex literals,.font(.system(...)), or hardcoded paddings/radii in feature code. The harness PreToolUse hook (.claude/hooks/ui-banned-list.sh) enforces this mechanically. - Reuse > extend > create. Before any new
struct X: View/ViewModifier/ variant, grepDesignSystem.swiftand run the/component-reuse-checkskill. Parallel implementations are the #1 drift in this codebase. - HIG compliance: all interactive elements ≥ 44×44pt; never color alone for meaning; honor
accessibilityReduceMotion. - No social features, no exercise discovery feed, or progression controls in the active logging flow. Suggestions appear only after the exercise or workout and require acceptance.
Unit/UI/DesignSystem.swiftis the only place raw values live (AppColor,AppFont,AppSpacing,AppRadius,AppIcon). Add new tokens there. Geist / Geist Mono are reached only viaAppFont.*cases — neverFont.custom("Geist…")directly.AppCardList(data) { row }is the canonical list-in-card primitive. Never composeAppCard { AppDividedList(...) }by hand — the hook blocks it.- Sheet roots are plain
VStackwithpresentationDetents. NoScrollVieworAppCardas the root child of.sheet { }. - Toolbar chrome defers to iOS-native — no
.weight(...)onToolbarItembuttons. appScrollEdgeSoft(top:bottom:)is the single canonical fade-behind-bar modifier. Never inline aLinearGradientor.maskfor the same effect.