|
| 1 | +# Afterimage — Runnable Proof Path |
| 2 | + |
| 3 | +A one-pass walkthrough from a clean checkout to a working in-simulator demo of |
| 4 | +the historical photo overlay. Each step has a command, an expected result, and |
| 5 | +a quick "if it fails" pointer. |
| 6 | + |
| 7 | +> **Audience:** anyone resuming after a long pause, demoing the app to someone |
| 8 | +> else, or capturing a baseline before changes. |
| 9 | +
|
| 10 | +--- |
| 11 | + |
| 12 | +## 0. Prerequisites |
| 13 | + |
| 14 | +- macOS (iOS toolchain required) |
| 15 | +- Xcode 26.3+ (matches `project.yml`) |
| 16 | +- Python 3.11+ (for `DataPipeline/`) |
| 17 | +- XcodeGen (`brew install xcodegen`) — `project.yml` is the source of truth |
| 18 | +- A real Apple developer account if you want to run on a device. Simulator |
| 19 | + works without one. |
| 20 | + |
| 21 | +```bash |
| 22 | +xcodebuild -version |
| 23 | +xcodegen --version |
| 24 | +python3 --version |
| 25 | +``` |
| 26 | + |
| 27 | +--- |
| 28 | + |
| 29 | +## 1. Regenerate the Xcode project from `project.yml` |
| 30 | + |
| 31 | +```bash |
| 32 | +cd /Users/d/Projects/Afterimage |
| 33 | +xcodegen generate |
| 34 | +``` |
| 35 | + |
| 36 | +**Expected:** `Afterimage.xcodeproj` is rebuilt against `project.yml`. No |
| 37 | +warnings. |
| 38 | + |
| 39 | +**If it fails:** `xcodegen --quiet generate` to see clean errors; usually a |
| 40 | +missing folder or new file not declared in `project.yml`. |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## 2. Build for simulator |
| 45 | + |
| 46 | +```bash |
| 47 | +xcodebuild \ |
| 48 | + -project Afterimage.xcodeproj \ |
| 49 | + -scheme Afterimage \ |
| 50 | + -configuration Debug \ |
| 51 | + -destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest' \ |
| 52 | + build |
| 53 | +``` |
| 54 | + |
| 55 | +**Expected:** `BUILD SUCCEEDED`. Warnings about deprecated APIs are OK if they |
| 56 | +are not new (compare against the last green commit `c29f1ef`). |
| 57 | + |
| 58 | +**If it fails:** |
| 59 | +- Verify `DEVELOPMENT_TEAM` is set in `project.yml` (commit `95f2915`). |
| 60 | +- `xcodebuild -showsdks` to confirm an iOS 17+ SDK is available. |
| 61 | +- Clean the build with `xcodebuild clean` then retry. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +## 3. Run the unit-test suite |
| 66 | + |
| 67 | +```bash |
| 68 | +xcodebuild \ |
| 69 | + -project Afterimage.xcodeproj \ |
| 70 | + -scheme Afterimage \ |
| 71 | + -destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest' \ |
| 72 | + test |
| 73 | +``` |
| 74 | + |
| 75 | +**Expected:** `TEST SUCCEEDED`. Coverage includes: |
| 76 | +- `DatabaseManagerTests` — SQLite open + schema |
| 77 | +- `HeadingFilterTests` — heading window filter (45° default, skip when |
| 78 | + `headingAccuracy > 45°`) |
| 79 | +- `MatchingServiceTests` — composite score (70% geo + 30% vision) |
| 80 | +- `SpatialQueryTests` — bounding-box + Haversine ≤100m |
| 81 | + |
| 82 | +**If it fails:** look at the test name; the test file is at |
| 83 | +`AfterimageTests/<TestName>.swift`. Most failures are fixture-data or |
| 84 | +GRDB-version-related. |
| 85 | + |
| 86 | +--- |
| 87 | + |
| 88 | +## 4. Verify the data pipeline (optional but recommended) |
| 89 | + |
| 90 | +The data pipeline builds the `photos.db` SQLite index that gets bundled into |
| 91 | +the app. It is **dev-time only** — not run on device. |
| 92 | + |
| 93 | +```bash |
| 94 | +cd /Users/d/Projects/Afterimage/DataPipeline |
| 95 | +python3 -m venv .venv |
| 96 | +source .venv/bin/activate |
| 97 | +pip install -r requirements.txt |
| 98 | + |
| 99 | +# Ingest one source as a smoke test (Wikimedia is the most stable) |
| 100 | +python3 ingest_wikimedia.py --city nyc |
| 101 | + |
| 102 | +# Build the index from all ingested rows |
| 103 | +python3 build_index.py |
| 104 | + |
| 105 | +# Coverage audit (gate that Phase 0 widens past 2 cities) |
| 106 | +python3 audit_coverage.py |
| 107 | +``` |
| 108 | + |
| 109 | +**Expected:** |
| 110 | +- `photos.db` produced under `DataPipeline/output/` |
| 111 | +- Coverage audit reports ≥25% of 100m grid cells covered for the cities you |
| 112 | + ingested |
| 113 | +- Commit `bfc2390` widened the pipeline to NYC, SF, Chicago — re-running with |
| 114 | + `--city all` should reproduce the 3-city dataset |
| 115 | + |
| 116 | +**If it fails:** |
| 117 | +- Wikimedia/LoC APIs throttle — wait, retry. The ingest scripts log rate-limit |
| 118 | + hits. |
| 119 | +- Memory ballooning during index build → reduce batch size in |
| 120 | + `build_index.py`. |
| 121 | + |
| 122 | +--- |
| 123 | + |
| 124 | +## 5. Replace the bundled `photos.db` (if you regenerated one in step 4) |
| 125 | + |
| 126 | +```bash |
| 127 | +cp DataPipeline/output/photos.db Afterimage/Resources/photos.db |
| 128 | +``` |
| 129 | + |
| 130 | +Rebuild (step 2) so Xcode picks up the new bundle resource. |
| 131 | + |
| 132 | +**Skip this step if you're demoing the as-shipped DB.** The bundle's existing |
| 133 | +`photos.db` is the one tied to the most recent commit. |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +## 6. Launch the app in simulator and walk the demo |
| 138 | + |
| 139 | +```bash |
| 140 | +open -a Simulator |
| 141 | +# In the simulator: Features > Location > Custom Location... |
| 142 | +# NYC: 40.7128, -74.0060 |
| 143 | +# SF: 37.7749, -122.4194 |
| 144 | +# Chicago: 41.8781, -87.6298 |
| 145 | +``` |
| 146 | + |
| 147 | +Then in Xcode: hit Run (Cmd-R) on the `Afterimage` scheme with the simulator |
| 148 | +selected. |
| 149 | + |
| 150 | +### Demo flow |
| 151 | + |
| 152 | +1. **Permission prompts** — Location, Camera. Accept both. Without Location, |
| 153 | + no spatial query; without Camera, only camera-roll mode works. |
| 154 | +2. **Capture or pick a photo**. For a quick simulator demo, use a known |
| 155 | + camera-roll photo set to the NYC location. (Simulator → Features > Photos |
| 156 | + > Add to Library, then set location via Features > Location.) |
| 157 | +3. **Wait for match**. The matching pipeline runs: |
| 158 | + - Spatial query (≤100m, ~20 candidates) |
| 159 | + - Heading filter (±45° if `headingAccuracy` is good) |
| 160 | + - Thumbnail fetch (Kingfisher, concurrent) |
| 161 | + - Vision feature-print ranking (composite score: 70% geo + 30% vision) |
| 162 | +4. **Slider reveal**. Drag the vertical slider on the comparison view. The |
| 163 | + historical image fades in beneath the present-day photo. |
| 164 | +5. **Share composite**. Tap Share → `UIActivityViewController` opens with the |
| 165 | + composite image rendered. |
| 166 | + |
| 167 | +### What "works" looks like |
| 168 | + |
| 169 | +- Match returns at least 1 candidate for the NYC test location |
| 170 | +- Slider drag is smooth (60 FPS) |
| 171 | +- Composite share renders both images |
| 172 | +- No crash, no permission loops |
| 173 | + |
| 174 | +--- |
| 175 | + |
| 176 | +## 7. Verify the security/privacy posture |
| 177 | + |
| 178 | +```bash |
| 179 | +# Privacy manifest present (commit 651a5f4) |
| 180 | +ls Afterimage/PrivacyInfo.xcprivacy |
| 181 | + |
| 182 | +# DEVELOPMENT_TEAM set for App Store signing (commit 95f2915) |
| 183 | +grep DEVELOPMENT_TEAM project.yml |
| 184 | + |
| 185 | +# No accounts / backend dependencies |
| 186 | +grep -r "https://api\." Afterimage --include='*.swift' | head -5 |
| 187 | +# Expected: empty or only Wikimedia thumbnail fetches |
| 188 | +``` |
| 189 | + |
| 190 | +--- |
| 191 | + |
| 192 | +## 8. App Store metadata sanity (commit `c29f1ef`) |
| 193 | + |
| 194 | +```bash |
| 195 | +cat APPSTORE-METADATA.md | head -20 |
| 196 | +``` |
| 197 | + |
| 198 | +Verify subtitle, description, keywords, and privacy questions match the |
| 199 | +intended pitch. Screenshots are committed separately when ready. |
| 200 | + |
| 201 | +--- |
| 202 | + |
| 203 | +## Build-proof source of truth |
| 204 | + |
| 205 | +This checklist mirrors the build proof captured at commits: |
| 206 | + |
| 207 | +- `bfc2390` — pipeline expanded to NYC, SF, Chicago |
| 208 | +- `78d9f1e` — vision: double continuation resume fix |
| 209 | +- `651a5f4` — privacy manifest |
| 210 | +- `95f2915` — DEVELOPMENT_TEAM for App Store signing |
| 211 | +- `c29f1ef` — App Store Connect metadata |
| 212 | + |
| 213 | +If a step regresses, bisect against these commits. |
| 214 | + |
| 215 | +--- |
| 216 | + |
| 217 | +## What "Phase 1" success means (per CLAUDE.md) |
| 218 | + |
| 219 | +Phase 1 is "Core App — Camera → Match → Slider". Success criteria for the |
| 220 | +runnable proof: |
| 221 | + |
| 222 | +| Capability | Verification step | |
| 223 | +|---|---| |
| 224 | +| Camera capture works | Step 6, action 2 | |
| 225 | +| Camera-roll picker works | Step 6, action 2 alternative | |
| 226 | +| MatchingService returns ≥1 candidate at known cities | Step 6, action 3 | |
| 227 | +| Slider overlay reveals historical image | Step 6, action 4 | |
| 228 | +| Share composite renders | Step 6, action 5 | |
| 229 | +| Privacy manifest present | Step 7 | |
| 230 | +| Tests pass | Step 3 | |
| 231 | + |
| 232 | +Phase 1 widening (more cities, more sources, ML re-ranking refinements) is |
| 233 | +out-of-scope here; this doc only proves the current state runs. |
0 commit comments