Imported from Sunnshiine/workout-app (
AGENTS.md). Install upstream withnpx skills add Sunnshiine/workout-app. Copyright stays with the author.
AGENTS.md
WorkoutTracker is an iOS client for powerlifting athletes. It surfaces and logs the workouts a
coach manages in a Google Sheet. The Sheet is the single source of truth and the app is a
read-write client with a local cache (ADR-0001). CLAUDE.md is a symlink to this file.
Sources of truth
Each of these wins over anything written here. Read the one that governs the work before starting.
CONTEXT.mdnames every domain term and the synonyms to avoid. Use its words in code, tests, and issues.docs/adr/records decisions. Read the ADRs for the area you change. If your change contradicts one, say so in the PR instead of overriding it.PRODUCT.mdandDESIGN.mdgovern product and UI work.CODING_STANDARDS.mdis the review standard. Read it before changing a store, a coordinator, a View's logic, or a test, and apply it at review..swift-formatand.swiftlint.ymlown formatting and every mechanical rule.docs/TESTING.mdowns the change-risk gate and flake hunting.tools/crap/README.mdand ADR-0016 own the gate's counting rules.Sources/WorkoutCLI/README.mdowns theworkoutCLI. ADR-0015 records the boundary it runs on..agents/skills/verify/SKILL.mddrives the app on the simulator and captures proof. Read the matching file under.agents/skills/verify/features/before driving.
Repository map
The directory a file sits in decides which builds compile it (ADR-0017). These entries are stable; the folders inside them move, so read the tree instead of a copy of it.
App/ The iOS app alone: entry point, Views, Live Activity controller, Google
sign-in, assets. Outside the package, so swift test never sees it.
Sources/WorkoutTracker/ The library, compiled into the app, the CLI, and swift test: the domain
model, Sheet parsing, sync and stores, session and progress logic, the
fixtures, and WorkoutApplication.
Sources/WorkoutCLI/ The workout executable. In neither the app nor the widget.
WorkoutShared/ Live Activity attributes, compiled into the app and the widget.
WorkoutWidgets/ The widget extension.
Tests/ Unit/ and Component/ run under swift test. UI/ and Visual/ run on the
simulator only. Support/ holds the fakes and fixtures both runs share.
tools/crap/ The CRAP scorer, its own package, run through scripts/crap.sh.
App/, Sources/WorkoutTracker/, WorkoutShared/, WorkoutWidgets/, and the folders under
Tests/ are Xcode buildable folders. A Swift file added there compiles into its target with no
project edit, and Xcode copies every other file in the folder into the bundle. To keep a file out
of the bundle, add a membership exception in the project, as each Info.plist has.
Boundaries
- UIKit and other iOS-only APIs belong in
App/. Every guard, calculation, and branch that decides behaviour belongs inSources/WorkoutTracker/, whereswift testand the CLI reach it. WorkoutApplicationis the composition root and the public facade. The app and the CLI both build on it (ADR-0015).- The Sheet is written only through
SyncCoordinatorand its pending-write queue (ADR-0006).
Commands
swift test # unit + component; no Secrets.xcconfig needed
swift test --filter ActiveSetFocusManagerTests # one file's tests, about a second
scripts/lint.sh # what CI runs, --strict; --fix autocorrects first
swift-format -i -r App/ Sources/ Tests/ # format
scripts/crap.sh gate # the change-risk gate CI runs (ADR-0016)
scripts/test-sim.sh unit # simulator suites from one build: unit | visual | ui | all
scripts/test-sim.sh --no-build WorkoutTrackerUITests/WorkoutTrackerUISmokeTests/testCurrentSessionLogsFirstSetAndAdvancesActiveSet
xcodebuild build -project WorkoutTracker.xcodeproj -scheme WorkoutTracker \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=27.0'
scripts/flake-hunt.sh --repetitions 1000 SyncCoordinatorTests # repeat a concurrent test under load
scripts/mutate.sh --filter <suite> <file> '<sed>' # which tests kill a mutant; --help for the form
The headless CLI drives the real stores against a local workbook and prints JSON, in milliseconds, with no simulator and no Google credentials:
swift build --product workout && export PATH="$PWD/.build/debug:$PATH"
export WORKOUT_HOME=$(mktemp -d) && workout init --scenario fresh-block
workout log w1d1.e0.s0 185x5@8 && workout flush && workout sheet --cell K15 # "185x5@8" landed
Verification
Run scripts/lint.sh and swift test before finishing. On every PR, CI runs those, the CRAP
gate, the simulator-hosted unit and component suite, and the visual gate. A PR whose changed paths
all sit in ci.yml's paths-ignore (Markdown, docs/, agent files, and more) starts no run.
- Neither test run is a superset of the other.
swift testcompilesSources/alone, so a green run does not prove the app compiles.scripts/test-sim.sh unitcompiles the app and skips the macOS-only CLI suites (WorkoutCLIBinaryTests,CLIFailureTests). Before pushing a diff that touchesApp/, run the simulator suite as well. - Run simulator suites through
scripts/test-sim.sh. A barexcodebuild testfails plug-in validation on a fresh machine and can hang collecting diagnostics after a failure. - Pin
OS=27.0in every-destination. A machine with two runtimes holds two devices named iPhone 17 Pro, and xcodebuild may pick the wrong one. - Concurrent UI-test sessions must not share a simulator. Give each its own UDID
(
-destination 'platform=iOS Simulator,id=<UDID>') and its own-derivedDataPathand-clonedSourcePackagesDirPath. - The
WorkoutTrackerscheme launches against local fixtures (-UITEST_FIXTURE true), never the live Sheet.Copy of WorkoutTrackerruns live. - Visual Baselines are recorded on the CI runner, not locally, because renders differ across
machines (ADR-0007; the recipe is in
docs/TESTING.md).
Worktrees and landing
Secrets.xcconfigis git-ignored and needed only for Xcode app builds;swift testruns without it.scripts/install-worktree-bootstrap.sh --source <path>installs the post-checkout hook that copies it into every new worktree. In a worktree the hook did not run in, runsh .githooks/post-checkoutonce.- XcodeBuildMCP session defaults point at the primary checkout. From a worktree, pass
-project <worktree>/WorkoutTracker.xcodeprojexplicitly. The.mcp.jsonpin stays at 2.7.0 or later, because older builds fail every accessibility call on Xcode 27. - Land with
scripts/ci-wait.sh Nand thengh pr merge N --squash. Leave out--delete-branch. GitHub deletes the remote branch itself, and the flag switches whichever worktree holds the branch ontomain.scripts/prune-merged-worktrees.shlists worktrees whose PR has merged or closed and removes them only under--apply.
Agent workflows
- Issues and PRDs live in GitHub Issues for
Sunnshiine/workout-app. The workflow isdocs/agents/issue-tracker.md, and the five triage labels aredocs/agents/triage-labels.md. - Sandcastle runs label-driven implementation and review in GitHub Actions. Prompts are in
.sandcastle/, workflows in.github/workflows/agent-*.yml, and the map isdocs/agents/sandcastle.md. - A UI prototype renders as HTML for a layout question, or ships to the phone through the
testflightlabel for a question of feel.docs/agents/prototyping.mdowns the decision.