Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
c9e0ade
Update app for Burning Man 2026
chrisballinger Jul 3, 2026
ed0b174
PlayaDB audit: correctness fixes + performance overhaul
chrisballinger Jul 3, 2026
60c65a9
PlayaDB: narrow observation regions + adopt DatabaseMigrator
chrisballinger Jul 3, 2026
1d65d0a
CLAUDE.md: commit validated chunks of work proactively
chrisballinger Jul 3, 2026
728e562
Previews: shared in-memory PlayaDB instead of on-disk connections
chrisballinger Jul 3, 2026
923f4c7
Docs: record preview fix + simulator sanity pass results
chrisballinger Jul 3, 2026
a8f8525
Add drive-app skill + critical-flow docs for simulator automation
chrisballinger Jul 4, 2026
421217e
Previews: deterministic cells + loading states for Art/Camp lists
chrisballinger Jul 4, 2026
7db10ba
watchOS Phase 0: standalone iBurnWatch target with on-watch PlayaDB seed
chrisballinger Jul 4, 2026
db8d707
watchOS Phase 1: offline Canvas map of BRC with compass mode
chrisballinger Jul 4, 2026
d00b659
watchOS Phases 3+4: Nearby, Favorites, Detail + compass Navigation
chrisballinger Jul 4, 2026
104791b
PlayaDB: maintain spatial R*Tree on gps column UPDATEs
chrisballinger Jul 4, 2026
c30f29d
watchOS: embed iBurnWatch as companion app in iBurn
chrisballinger Jul 4, 2026
e2b1edb
PlayaDB: reseed when bundled data is newer than imported data
chrisballinger Jul 4, 2026
db1afbd
Docs: record on-device verification of reseed fix
chrisballinger Jul 4, 2026
947eb14
watchOS: keep map on the city when the user is off-playa
chrisballinger Jul 7, 2026
a2cc6fa
Fix all Swift concurrency build warnings
chrisballinger Jul 7, 2026
8afaab3
Docs: record warning-fix session
chrisballinger Jul 7, 2026
125b1c7
Xcode project housekeeping from watch device deploy
chrisballinger Jul 7, 2026
6bd261f
watchOS: double-tap zoom, app icon, install name "iBurn"
chrisballinger Jul 7, 2026
dfd3ebe
Docs: record watch polish session
chrisballinger Jul 7, 2026
99eb835
Docs: architecture analysis + 2026 season / 2027 migration roadmap
chrisballinger Jul 7, 2026
ce56010
SwiftUI lists default ON: complete the list-tab YapDB migration
chrisballinger Jul 11, 2026
cf70499
watchOS Phase 2: phone<->watch favorites sync over WatchConnectivity
chrisballinger Jul 12, 2026
2f5c840
Docs: mark watch MVP Phase 2 complete
chrisballinger Jul 12, 2026
fd00615
watchOS: browse all data types + visit status ("want to visit") every…
chrisballinger Jul 12, 2026
0b39f28
Map: official 2026 BMorg tiles + stale tile-cache fix
chrisballinger Jul 14, 2026
e56a543
Bump iBurn-Data: 2026 API data refresh (July 18)
chrisballinger Jul 18, 2026
f8c53f7
Docs: 2026 API data refresh session notes
chrisballinger Jul 18, 2026
17addfc
Bump iBurn-Data: mv fetch support + refreshed mv data
chrisballinger Jul 18, 2026
d4064a0
Docs: mv fetch support in data refresh notes
chrisballinger Jul 18, 2026
a8b07f3
PlayaGeo street labels + watch MapKit-style tracking button
chrisballinger Jul 18, 2026
b211090
Fix stale first-row after event day switch (wrong weekday + dead tap)
chrisballinger Jul 18, 2026
ed92210
Restore bundled YapDB seed for 2026 first launch (iBurn-2026.zip)
chrisballinger Jul 18, 2026
5ae3fa9
PlayaAPI: lenient decoding for user-entered URL fields
chrisballinger Jul 18, 2026
f08d6c3
Pre-populated PlayaDB seed restore (PlayaDB-2026.zip)
chrisballinger Jul 18, 2026
dc37275
Events tab: max-duration filter to hide amenity-listing pseudo-events
chrisballinger Jul 18, 2026
3c2065b
Fix stale first row on same-day filter changes (anchor id collision)
chrisballinger Jul 18, 2026
48dcf9c
Filter sheet: tap-to-set duration slider + Reset button
chrisballinger Jul 18, 2026
f29d84f
Events list: remove row .id() entirely + guard stale observation emis…
chrisballinger Jul 19, 2026
8cbdbde
Bump iBurn-Data: 2026 tiles from refreshed BMorg GeoJSON + Gate Road
chrisballinger Jul 25, 2026
156ae24
Bump iBurn-Data: 2026 geocoder validated vs BMorg GIS; Rod's Road fix
chrisballinger Jul 25, 2026
b48ef7b
Bump iBurn-Data: C street is Ceiba, Rod's Road removed (2026 correcti…
chrisballinger Jul 25, 2026
ce4e7fc
watchOS: user map pins synced with the phone + system map controls
chrisballinger Jul 25, 2026
53c47cf
PlayaDB: v4 audio_tour_url + v5 event_calendar_entries
chrisballinger Jul 25, 2026
d591aa8
Fix Yap/PlayaDB bridge bugs found in the migration audit
chrisballinger Jul 25, 2026
56faf12
Map: rebuild the visible-pins list on PlayaDB
chrisballinger Jul 25, 2026
a92aba7
Visit List and Audio Tour on PlayaDB by default
chrisballinger Jul 25, 2026
a60fe7f
Own event calendar entries in PlayaDB, not Yap metadata
chrisballinger Jul 25, 2026
5c9ae27
Docs: 2026 Yap->PlayaDB default audit, migration record, flows
chrisballinger Jul 25, 2026
9e01739
CLAUDE.md: point build commands at the iOS 26.5 simulator
chrisballinger Jul 25, 2026
a05ca4d
Bump iBurn-Data: geocoder now reads BMorg's official GeoJSON
chrisballinger Jul 25, 2026
cacfda0
Docs: handoff for the org-GeoJSON geocoder branch
chrisballinger Jul 25, 2026
ea42f65
Docs: merge handoff for the watch pins branch
chrisballinger Jul 25, 2026
549d047
Merge branch 'reverse-geocoder' into 2026-updates
chrisballinger Jul 25, 2026
6ca89f4
Merge branch 'watchos-updates' into 2026-updates
chrisballinger Jul 25, 2026
40e362d
Docs: trim CLAUDE.md to non-derivable guidance
chrisballinger Jul 25, 2026
c90c54c
Fix archive dependency cycle and restore the onboarding video
chrisballinger Jul 26, 2026
ca68d41
playa-seed: build the pre-baked PlayaDB seed with colors precomputed
chrisballinger Jul 26, 2026
adb4011
watchOS: restore the pre-baked PlayaDB seed too
chrisballinger Jul 26, 2026
7107dd3
Two-tier location embargo per BMorg API ToS
chrisballinger Aug 7, 2026
b134f47
iBurn-Data: Aug 6 API refresh, DMZ2 + toilets GIS update, new tiles
chrisballinger Aug 7, 2026
5987f31
Docs: record Aug 6 embargo/data-refresh session; update flow doc
chrisballinger Aug 7, 2026
5e857d4
Guard against shipping mock placement data; document fixture workflow
chrisballinger Aug 7, 2026
fbddd0b
Point iBurn-Data submodule at the private repo for now
chrisballinger Aug 7, 2026
b23c8b9
Update settings
chrisballinger Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"worktree": {
"bgIsolation": "none"
}
}
113 changes: 113 additions & 0 deletions .claude/skills/drive-app/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
name: drive-app
description: Build, launch, and drive the iBurn app in the iOS Simulator via XcodeBuildMCP UI automation — for verifying flows end-to-end, taking screenshots, and sanity-passing changes. Use when asked to run the app, exercise/verify a user flow, reproduce a UI bug, or validate database behavior on-device. Flow-by-flow steps live in references/flows.md.
---

# Driving the iBurn app in the Simulator

## Prerequisites

1. **XcodeBuildMCP** must be connected with the `simulator` and `ui-automation`
workflows. `.xcodebuildmcp/config.yaml` in this repo already enables
`["simulator", "device", "ui-automation"]`. If `tap` / `type_text` / `swipe`
tools are missing from ToolSearch, the server predates the config — ask the
user to run `/mcp` → reconnect XcodeBuildMCP.
2. Call `session_show_defaults` first. If workspace/scheme/simulator/bundleId are
not set, set them:
- workspacePath: `/Users/chrisbal/Documents/Code/iBurn-iOS/iBurn.xcworkspace`
- scheme: `iBurn`
- simulator: iPhone 17 Pro Max (look up the UDID with `list_sims`)
- bundleId: `com.trailbehind.iBurn2010`

## Critical setup facts (learned the hard way)

- **The SwiftUI/PlayaDB stack is ON by default.** The flag
`featureFlag.lists.useSwiftUI` (all builds, default true) acts as a
kill-switch: set it to NO and you get legacy UIKit/YapDatabase screens and
`PlayaDB.sqlite` is never created. To exercise the legacy stack, set before
(re)launching:
```bash
xcrun simctl spawn <UDID> defaults write com.trailbehind.iBurn2010 featureFlag.lists.useSwiftUI -bool NO
```
- **PlayaDB seeds lazily**, when the DependencyContainer is first built (tab
construction after onboarding) — not at app launch. Don't conclude seeding is
broken because the DB file doesn't exist yet; navigate into the main UI first.
- **`print()` output is not captured** in build_run_sim runtime logs (NSLog only).
Verify database state by querying the on-sim SQLite file directly (below)
instead of hunting for log lines.
- For **first-launch flows**, erase the simulator first:
`xcrun simctl shutdown <UDID>; xcrun simctl erase <UDID>`.
- For **Nearby/location flows**, set a Black Rock City location:
`xcrun simctl location <UDID> set 40.7864,-119.2065`.

## Interaction workflow

Observe with `snapshot_ui`, act with `tap`/`swipe`/`type_text` on elementRefs
from the latest snapshot, re-snapshot after navigation. Gotchas specific to this
app:

- **Onboarding carousel pages advance by swiping LEFT** on the page scroll-view.
The action button only works on pages that request permissions; on info-only
pages (Search, Nearby) tapping it does nothing — swipe instead.
- **Permission alerts arrive in this order** on a fresh install: notifications
(springboard alert), then in onboarding: location → notifications
(PermissionScope buttons) → calendar full-access. See flows.md for exact steps.
- After onboarding, an **embargo alert** ("Locations Are Hidden") appears over the
map — dismiss via "Ok cool whatever".
- **Favorite hearts are not in the accessibility tree.** To verify heart state in
a list, take a `screenshot` and inspect the image; don't grep the AX snapshot.
- **SwiftUI searchable fields flicker in and out of the AX tree** (the
"Search events" field may not be listed after scrolling). Prefer validating
search at the database layer (FTS MATCH query below) unless the search UI
itself is under test.

## Verifying database state directly

```bash
APP_DATA=$(xcrun simctl get_app_container <UDID> com.trailbehind.iBurn2010 data)
sqlite3 "file:$APP_DATA/Documents/PlayaDB.sqlite?mode=ro" "
PRAGMA journal_mode; -- expect: wal
SELECT COUNT(*) FROM art_objects; -- ~321 (2026 data)
SELECT COUNT(*) FROM camp_objects; -- ~1201
SELECT COUNT(*) FROM event_objects; -- ~2101
SELECT COUNT(*) FROM event_occurrences; -- ~4431
SELECT identifier FROM grdb_migrations; -- v1-initial-schema
SELECT object_type, object_id, is_favorite FROM object_metadata;"
```

Invariants worth asserting after UI actions:
- Favoriting an **event** writes exactly one `object_metadata` row keyed by the
**parent event uid** (a real uid from `event_objects`, never a synthesized
`"<uid>_<n>"` form).
- Plain browsing/fetching must **not** create `object_metadata` rows (reads are
write-free).
- FTS health: `INSERT INTO event_objects_fts(event_objects_fts)
VALUES('integrity-check')` must not error; `MATCH` is stemmed and
case-insensitive.

These `sqlite3`/`simctl` commands need the Bash sandbox disabled (simulator
container paths are outside the sandbox allowlist).

## Flow catalog

Step-by-step scripts for the critical flows (onboarding, events browsing,
favoriting, search, map/embargo, detail, feature flags) live in
[references/flows.md](references/flows.md). Read it before driving a flow.

## Physical devices

Running on real hardware (enabling the `device` workflow, discovery, code
signing) is covered in
[references/device-deploy.md](references/device-deploy.md).

## Keeping the flow docs current

These docs are maintained by whoever notices drift, in the session where they
notice it:

- If a flow in `references/flows.md` doesn't match what the app actually does
(renamed screens, reordered onboarding, moved buttons, new permission prompts),
**update the doc in the same session** and commit it with your other changes.
- If you add or materially change a user-facing flow, add/update its entry in
`references/flows.md` as part of that change.
- Record data-shape drift too (seeded row counts change every festival year).
41 changes: 41 additions & 0 deletions .claude/skills/drive-app/references/device-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Running iBurn on a physical device

Simulator work is the default (see [../SKILL.md](../SKILL.md)); this file covers
the extra setup needed to build, install, and launch on real hardware.

## Enabling the device workflow

XcodeBuildMCP only exposes device tools when the `device` workflow is enabled in
`.xcodebuildmcp/config.yaml`:

```yaml
schemaVersion: 1
enabledWorkflows: ["simulator", "device"]
```

After creating or modifying this file, restart the XcodeBuildMCP MCP server
(`/mcp` → reconnect in Claude Code).

## Device discovery

```bash
# List connected physical devices (USB or network)
xcrun devicectl list devices
```

## XcodeBuildMCP device workflow

1. `list_devices` — List connected devices and their UDIDs
2. `session_set_defaults` — Set workspace, scheme, and `deviceId` (UDID)
3. `build_run_device` — Build, install, and launch on device (single step)
4. `launch_app_device` — Launch an already-installed app
5. `start_device_log_cap` / `stop_device_log_cap` — Capture device logs
6. `test_device` — Run tests on the physical device

## Requirements

- Code signing must be configured in Xcode for the target device
- Device must have Developer Mode enabled
- Device must be unlocked for app launch to succeed
- Device builds need the Bash sandbox disabled — the sandbox hides the Keychain,
so `codesign` can't find the iOS Development certificate
Loading