@shaykec/app-agent 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/.claude/agents/android-customizer.md +9 -1
  2. package/.claude/agents/catalog-analyzer.md +57 -0
  3. package/.claude/agents/ios-customizer.md +9 -1
  4. package/.claude/agents/react-native-customizer.md +71 -0
  5. package/.claude/skills/android-customizer/SKILL.md +108 -23
  6. package/.claude/skills/bug-fixer/SKILL.md +59 -0
  7. package/.claude/skills/catalog-analyzer/SKILL.md +96 -0
  8. package/.claude/skills/customization-planner/SKILL.md +44 -5
  9. package/.claude/skills/design-selector/SKILL.md +3 -1
  10. package/.claude/skills/design-system/SKILL.md +1 -1
  11. package/.claude/skills/exploratory-tester/SKILL.md +82 -0
  12. package/.claude/skills/ios-customizer/SKILL.md +123 -23
  13. package/.claude/skills/module-integrator/SKILL.md +1 -1
  14. package/.claude/skills/react-native-customizer/SKILL.md +97 -11
  15. package/.claude/skills/test-planner/SKILL.md +72 -0
  16. package/.cursor/agents/README.md +3 -1
  17. package/.cursor/agents/android-customizer.md +15 -11
  18. package/.cursor/agents/catalog-analyzer.md +83 -0
  19. package/.cursor/agents/ios-customizer.md +15 -10
  20. package/.cursor/agents/react-native-customizer.md +170 -0
  21. package/.cursor/mcp.json +2 -10
  22. package/.cursor/rules/safety-guardrails.mdc +1 -1
  23. package/.cursor/rules/workflow.mdc +52 -18
  24. package/.cursor/skills/android-customizer/SKILL.md +46 -22
  25. package/.cursor/skills/bug-fixer/SKILL.md +189 -0
  26. package/.cursor/skills/catalog-analyzer/SKILL.md +222 -0
  27. package/.cursor/skills/customization-planner/SKILL.md +55 -8
  28. package/.cursor/skills/design-selector/SKILL.md +6 -5
  29. package/.cursor/skills/design-system/SKILL.md +8 -7
  30. package/.cursor/skills/exploratory-tester/SKILL.md +223 -0
  31. package/.cursor/skills/ios-customizer/SKILL.md +50 -15
  32. package/.cursor/skills/module-integrator/SKILL.md +2 -2
  33. package/.cursor/skills/output-validator/SKILL.md +1 -1
  34. package/.cursor/skills/react-native-customizer/SKILL.md +115 -25
  35. package/.cursor/skills/test-planner/SKILL.md +199 -0
  36. package/AGENTS.md +32 -11
  37. package/CLAUDE.md +78 -33
  38. package/README.md +77 -11
  39. package/designs/DESIGN_CATALOG.md +17 -15
  40. package/designs/DESIGN_PRINCIPLES.md +53 -0
  41. package/designs/brands/accessible-high-contrast.md +14 -0
  42. package/designs/brands/corporate-professional.md +14 -0
  43. package/designs/brands/dark-luxe.md +14 -0
  44. package/designs/brands/kids-playful.md +14 -0
  45. package/designs/brands/medical-clinical.md +14 -0
  46. package/designs/brands/modern-minimal.md +14 -0
  47. package/designs/brands/nature-organic.md +14 -0
  48. package/designs/brands/neo-brutalist.md +14 -0
  49. package/designs/brands/retro-vintage.md +14 -0
  50. package/designs/brands/soft-gradient.md +14 -0
  51. package/designs/brands/sport-athletic.md +14 -0
  52. package/designs/brands/tech-dynamic.md +14 -0
  53. package/designs/brands/vibrant-playful.md +14 -0
  54. package/dist/cli.d.ts +4 -2
  55. package/dist/cli.d.ts.map +1 -1
  56. package/dist/cli.js +91 -1
  57. package/dist/cli.js.map +1 -1
  58. package/dist/config.d.ts +2 -0
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +2 -0
  61. package/dist/config.js.map +1 -1
  62. package/dist/engines/claude-engine.d.ts.map +1 -1
  63. package/dist/engines/claude-engine.js +16 -4
  64. package/dist/engines/claude-engine.js.map +1 -1
  65. package/dist/engines/types.d.ts +1 -1
  66. package/dist/engines/types.d.ts.map +1 -1
  67. package/dist/engines/types.js +31 -2
  68. package/dist/engines/types.js.map +1 -1
  69. package/dist/github.d.ts +3 -0
  70. package/dist/github.d.ts.map +1 -1
  71. package/dist/github.js +47 -4
  72. package/dist/github.js.map +1 -1
  73. package/dist/index.js +217 -9
  74. package/dist/index.js.map +1 -1
  75. package/dist/prompt-builder.d.ts +11 -1
  76. package/dist/prompt-builder.d.ts.map +1 -1
  77. package/dist/prompt-builder.js +216 -1
  78. package/dist/prompt-builder.js.map +1 -1
  79. package/dist/validator.d.ts +7 -2
  80. package/dist/validator.d.ts.map +1 -1
  81. package/dist/validator.js +61 -41
  82. package/dist/validator.js.map +1 -1
  83. package/dist/workspace.js +2 -2
  84. package/dist/workspace.js.map +1 -1
  85. package/package.json +2 -2
  86. package/prompts/agent-prompt.md +35 -18
  87. package/prompts/deep-test-agent-prompt.md +122 -0
  88. package/prompts/fix-agent-prompt.md +90 -0
  89. package/prompts/quick-agent-prompt.md +32 -2
  90. package/prompts/scratch-agent-prompt.md +5 -8
  91. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  92. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  93. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ConversationsScreen.kt +1 -1
  94. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  95. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/navigation/MainScreen.kt +1 -0
  96. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  97. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/navigation/MainNavigation.kt +5 -1
  98. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  99. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  100. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/MotionPreferencesScreen.kt +3 -3
  101. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/navigation/Navigation.kt +1 -1
  102. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/settings/SettingsScreen.kt +1 -1
  103. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  104. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  105. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  106. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  107. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/settings/SettingsScreen.kt +3 -2
  108. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  109. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/features/settings/SettingsScreen.kt +1 -1
  110. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  111. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/cart/CartScreen.kt +3 -2
  112. package/templates/android/Skeleton/TESTING_MANIFEST.md +2 -1
  113. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/MainActivity.kt +23 -2
  114. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  115. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/theme/AppearanceManager.kt +42 -0
  116. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/profile/ProfileScreen.kt +20 -8
  117. package/templates/android/Skeleton/tests/03_detail_screen.yaml +3 -2
  118. package/templates/android/Skeleton/tests/04_favorites.yaml +3 -2
  119. package/templates/android/Skeleton/tests/08_full_e2e.yaml +9 -2
  120. package/templates/android/Skeleton/tests/09_dark_mode.yaml +50 -0
  121. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  122. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  123. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/settings/SettingsScreen.kt +3 -2
  124. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  125. package/templates/ios/BookTemplate/BookTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  126. package/templates/ios/ChatTemplate/ChatTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  127. package/templates/ios/DashTemplate/DashTemplate/App/AppConfig.swift +1 -0
  128. package/templates/ios/DashTemplate/DashTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  129. package/templates/ios/DashTemplate/DashTemplate/Core/Strings.swift +13 -0
  130. package/templates/ios/DashTemplate/DashTemplate.xcodeproj/project.pbxproj +32 -20
  131. package/templates/ios/FamilyTemplate/FamilyTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  132. package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  133. package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Strings.swift +42 -0
  134. package/templates/ios/FinanceTemplate/FinanceTemplate.xcodeproj/project.pbxproj +36 -30
  135. package/templates/ios/GameTemplate/GameTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  136. package/templates/ios/HealthTemplate/HealthTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  137. package/templates/ios/LearnTemplate/LearnTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  138. package/templates/ios/MapTemplate/MapTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  139. package/templates/ios/MediaTemplate/MediaTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  140. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  141. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Strings.swift +12 -0
  142. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Features/SkeletonLoading/SkeletonLoadingView.swift +2 -37
  143. package/templates/ios/ShopTemplate/ShopTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  144. package/templates/ios/Skeleton/Skeleton/Core/Animation/AnimatedTransitionsView.swift +201 -0
  145. package/templates/ios/Skeleton/tests/08_full_e2e.yaml +4 -0
  146. package/templates/ios/Skeleton/tests/09_dark_mode.yaml +52 -0
  147. package/templates/ios/SocialTemplate/SocialTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  148. package/templates/ios/TaskTemplate/TaskTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  149. package/templates/ios/TrackTemplate/TrackTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  150. package/templates/react-native/BookTemplate/src/animation/useAnimatedList.ts +219 -2
  151. package/templates/react-native/BookTemplate/src/animation/useMotionPreferences.ts +23 -9
  152. package/templates/react-native/BookTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  153. package/templates/react-native/ChatTemplate/src/animation/useAnimatedList.ts +219 -2
  154. package/templates/react-native/ChatTemplate/src/animation/useMotionPreferences.ts +23 -9
  155. package/templates/react-native/ChatTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  156. package/templates/react-native/DashTemplate/src/animation/useAnimatedList.ts +219 -2
  157. package/templates/react-native/DashTemplate/src/animation/useMotionPreferences.ts +23 -9
  158. package/templates/react-native/DashTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  159. package/templates/react-native/FamilyTemplate/src/animation/useAnimatedList.ts +219 -2
  160. package/templates/react-native/FamilyTemplate/src/animation/useMotionPreferences.ts +23 -9
  161. package/templates/react-native/FamilyTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  162. package/templates/react-native/FinanceTemplate/src/animation/useAnimatedList.ts +219 -2
  163. package/templates/react-native/FinanceTemplate/src/animation/useMotionPreferences.ts +23 -9
  164. package/templates/react-native/FinanceTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  165. package/templates/react-native/GameTemplate/src/animation/useAnimatedList.ts +219 -2
  166. package/templates/react-native/GameTemplate/src/animation/useMotionPreferences.ts +23 -9
  167. package/templates/react-native/GameTemplate/src/screens/GameDetail/GameDetailScreen.tsx +2 -1
  168. package/templates/react-native/GameTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  169. package/templates/react-native/HealthTemplate/src/animation/useAnimatedList.ts +219 -2
  170. package/templates/react-native/HealthTemplate/src/animation/useMotionPreferences.ts +23 -9
  171. package/templates/react-native/HealthTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  172. package/templates/react-native/HealthTemplate/src/screens/WorkoutDetail/WorkoutDetailScreen.tsx +1 -1
  173. package/templates/react-native/LearnTemplate/src/animation/useAnimatedList.ts +219 -2
  174. package/templates/react-native/LearnTemplate/src/animation/useMotionPreferences.ts +23 -9
  175. package/templates/react-native/LearnTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  176. package/templates/react-native/MapTemplate/src/animation/useAnimatedList.ts +219 -2
  177. package/templates/react-native/MapTemplate/src/animation/useMotionPreferences.ts +23 -9
  178. package/templates/react-native/MapTemplate/src/screens/Map/MapScreen.tsx +14 -0
  179. package/templates/react-native/MapTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  180. package/templates/react-native/MediaTemplate/src/animation/useAnimatedList.ts +219 -2
  181. package/templates/react-native/MediaTemplate/src/animation/useMotionPreferences.ts +23 -9
  182. package/templates/react-native/MediaTemplate/src/screens/PlaylistDetail/PlaylistDetailScreen.tsx +1 -1
  183. package/templates/react-native/MediaTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  184. package/templates/react-native/ReferenceTemplate/src/animation/useAnimatedList.ts +219 -2
  185. package/templates/react-native/ReferenceTemplate/src/animation/useMotionPreferences.ts +23 -9
  186. package/templates/react-native/ReferenceTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
  187. package/templates/react-native/ShopTemplate/src/animation/useAnimatedList.ts +219 -2
  188. package/templates/react-native/ShopTemplate/src/animation/useMotionPreferences.ts +23 -9
  189. package/templates/react-native/ShopTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  190. package/templates/react-native/Skeleton/TESTING_MANIFEST.md +2 -1
  191. package/templates/react-native/Skeleton/src/animation/useAnimatedList.ts +219 -2
  192. package/templates/react-native/Skeleton/src/animation/useMotionPreferences.ts +23 -9
  193. package/templates/react-native/Skeleton/src/screens/Profile/ProfileScreen.tsx +1 -1
  194. package/templates/react-native/Skeleton/tests/07_profile.yaml +3 -2
  195. package/templates/react-native/Skeleton/tests/08_full_e2e.yaml +12 -1
  196. package/templates/react-native/Skeleton/tests/09_dark_mode.yaml +46 -0
  197. package/templates/react-native/SocialTemplate/src/animation/useAnimatedList.ts +219 -2
  198. package/templates/react-native/SocialTemplate/src/animation/useMotionPreferences.ts +23 -9
  199. package/templates/react-native/SocialTemplate/src/screens/Feed/FeedScreen.tsx +1 -0
  200. package/templates/react-native/SocialTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  201. package/templates/react-native/TaskTemplate/src/animation/useAnimatedList.ts +219 -2
  202. package/templates/react-native/TaskTemplate/src/animation/useMotionPreferences.ts +23 -9
  203. package/templates/react-native/TaskTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  204. package/templates/react-native/TrackTemplate/src/animation/useAnimatedList.ts +219 -2
  205. package/templates/react-native/TrackTemplate/src/animation/useMotionPreferences.ts +23 -9
  206. package/templates/react-native/TrackTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
  207. package/templates/shared/ios/AnimatedTransitions/AnimatedTransitionsView.swift +233 -93
  208. package/.claude/agents/template-selector.md +0 -39
  209. package/.claude/skills/module-selector/SKILL.md +0 -81
  210. package/.claude/skills/template-selector/SKILL.md +0 -44
  211. package/.cursor/agents/template-selector.md +0 -52
  212. package/.cursor/skills/module-selector/SKILL.md +0 -135
  213. package/.cursor/skills/template-selector/SKILL.md +0 -123
@@ -0,0 +1,223 @@
1
+ ---
2
+ name: exploratory-tester
3
+ description: Screenshot and accessibility-tree driven exploratory testing. Does NOT read source code — tests the app purely through what the AI sees on screen and what the accessibility tree reveals. Discovers visual bugs, accessibility issues, UX problems, and content issues that structured testing misses.
4
+ ---
5
+
6
+ # Exploratory Tester Skill
7
+
8
+ Use this skill to test the app by exploring it visually — using screenshots and the accessibility/element tree as your only inputs. You do NOT read source code. You test what a real user would see and interact with.
9
+
10
+ ## When to Use
11
+
12
+ - As Phase 2 of the `--deep-test` pipeline
13
+ - Runs in parallel with structured testing (Phase 3) on a separate simulator
14
+ - After the app is built and installed on a simulator
15
+
16
+ ## Context You Need
17
+
18
+ - **Platform** — `ios`, `android`, or `react-native`
19
+ - **Bundle ID / Package name** — e.g., `com.pawspa.app`
20
+ - **Simulator/device name** — the `deviceName` for your assigned simulator
21
+ - **App directory** — path under `output/` (only for writing the bug report, NOT for reading code)
22
+
23
+ **IMPORTANT:** You must NOT read source code files (Views, ViewModels, Models, etc.). Your only inputs are what `inspect()` gives you — screenshots and the element tree. This forces you to test like a real user.
24
+
25
+ ## Instructions
26
+
27
+ ### Phase A — Screen Discovery
28
+
29
+ Start by launching the app and discovering all reachable screens:
30
+
31
+ 1. **Launch the app:**
32
+ ```
33
+ inspect({ platform: "{platform}", app: "{bundleId}", deviceName: "{sim}" })
34
+ ```
35
+
36
+ 2. **Wait 3-5 seconds** for the app to fully render, then `inspect()` again.
37
+
38
+ 3. **Map the tab bar** — identify all tabs from the element tree. Record their labels.
39
+
40
+ 4. **Visit each tab** — tap each tab, `inspect()` on each to capture the screen and element tree.
41
+
42
+ 5. **Discover sub-screens** — from each tab, tap list items, buttons, and links to discover detail screens, modals, forms. Keep a visited-screens list to avoid loops.
43
+
44
+ 6. **Build a screen map:**
45
+ ```
46
+ Tab 1 ("Home") → HomeScreen → DetailScreen (from list item)
47
+ Tab 2 ("Explore") → ExploreScreen → DetailScreen (from search result)
48
+ Tab 3 ("Favorites") → FavoritesScreen
49
+ Tab 4 ("Profile") → ProfileScreen → SettingsScreen
50
+ ```
51
+
52
+ ### Phase B — Per-Screen Exploratory Audit
53
+
54
+ For **each discovered screen**, perform the following audit:
55
+
56
+ #### B1 — Visual Analysis (from screenshot)
57
+
58
+ After each `inspect()`, analyze what you see:
59
+
60
+ - **Layout correctness:**
61
+ - No overlapping text or elements
62
+ - No cut-off labels or truncated content that loses meaning
63
+ - Proper spacing between elements (not cramped, not too sparse)
64
+ - Content aligned correctly (left-aligned text, centered headers, etc.)
65
+ - No elements extending beyond screen bounds
66
+
67
+ - **Content quality:**
68
+ - Real domain-specific text (not "Lorem ipsum", "Item 1", "Product", "Template")
69
+ - Images appear to be loaded (no broken image placeholders, no empty squares)
70
+ - Icons match their context (not generic or mismatched)
71
+ - Consistent typography across the screen
72
+
73
+ - **Design consistency:**
74
+ - Colors match between elements on the same screen
75
+ - Typography hierarchy makes sense (headers larger than body text)
76
+ - Consistent spacing and padding across similar elements
77
+ - Navigation bar style is consistent across screens
78
+
79
+ - **Template leftover detection:**
80
+ - Look for ANY generic text that doesn't match the app's domain
81
+ - Common leftovers: "ShopTemplate", "TrackTemplate", "SocialTemplate", "Template", "Example", "Sample", "Demo", "Test Item", "Lorem", "Placeholder"
82
+ - Check headers, list items, buttons, placeholders, empty state text
83
+
84
+ #### B2 — Accessibility Tree Audit
85
+
86
+ Parse the element tree from `inspect()` and audit:
87
+
88
+ - **Labels on interactive elements:**
89
+ - Every button must have a non-empty accessibility label
90
+ - Every link/tappable element must have a descriptive label
91
+ - Labels should describe the action, not just "button" or "element"
92
+
93
+ - **Image descriptions:**
94
+ - Images should have accessibility descriptions
95
+ - Decorative images can be marked as such, but content images need descriptions
96
+
97
+ - **Form field labels:**
98
+ - Text fields must have associated labels or placeholder text
99
+ - Pickers and selectors must be labeled
100
+
101
+ - **Orphaned elements:**
102
+ - Elements in the a11y tree that are not visible on screen (hidden but accessible)
103
+ - Elements visible on screen but not in the a11y tree (visible but not accessible)
104
+
105
+ - **Touch target sizes:**
106
+ - Interactive elements should be at least 44x44pt (iOS) or 48x48dp (Android)
107
+ - Flag any buttons or tappable elements that appear too small
108
+
109
+ #### B3 — Interactive Exploration
110
+
111
+ Interact with EVERY discoverable element and observe what happens:
112
+
113
+ 1. **Tap every button** — verify something happens (navigation, modal, state change, animation). Record any buttons that do nothing ("dead buttons").
114
+
115
+ 2. **Type into every text field:**
116
+ - Normal text: verify it appears correctly
117
+ - Edge cases: empty submit, very long text (50+ characters), special characters (@#$%)
118
+ - Search fields: type a query and verify results update
119
+
120
+ 3. **Toggle every switch/checkbox** — verify state changes visually.
121
+
122
+ 4. **Scroll every list:**
123
+ - Scroll to the bottom — verify all items render
124
+ - Scroll back to the top — verify the header/nav bar is intact (no overlap bugs)
125
+
126
+ 5. **Try unexpected interactions:**
127
+ - Swipe on non-swipeable areas — should not cause crashes or weird behavior
128
+ - Double-tap buttons — should not trigger the action twice (unless intentional)
129
+ - Tap on non-interactive text — should not crash
130
+
131
+ 6. **Test navigation depth:**
132
+ - Navigate 3+ levels deep (tab → list item → detail → sub-detail)
133
+ - Navigate back all the way — verify each screen restores correctly
134
+ - Verify the back button works at every level
135
+
136
+ 7. **Test state persistence:**
137
+ - Toggle a favorite, navigate away, come back — is it still toggled?
138
+ - Enter text in a form, switch tabs, come back — is the text preserved?
139
+ - Scroll down in a list, switch tabs, come back — is the scroll position preserved?
140
+
141
+ 8. **Test tab switching:**
142
+ - Switch between all tabs rapidly
143
+ - Verify each tab shows its correct content (not stale data from another tab)
144
+ - Verify the selected tab indicator updates correctly
145
+
146
+ #### B4 — After-Interaction Comparison
147
+
148
+ After completing interactions on a screen:
149
+
150
+ 1. `inspect()` again to capture the post-interaction state
151
+ 2. Compare the element tree with the pre-interaction tree
152
+ 3. Flag any unexpected changes:
153
+ - Elements that disappeared when they shouldn't have
154
+ - New elements that appeared unexpectedly
155
+ - Element labels that changed incorrectly
156
+ - Count mismatches (e.g., list had 5 items, now has 4)
157
+
158
+ ### Phase C — Bug Classification and Reporting
159
+
160
+ For each issue discovered, classify it:
161
+
162
+ | Tag | Category | Description | Severity Guide |
163
+ |-----|----------|-------------|----------------|
164
+ | `[VISUAL]` | Visual | Layout, overlap, truncation, wrong colors | Medium if minor, High if content is unreadable |
165
+ | `[A11Y]` | Accessibility | Missing labels, unreachable elements, small targets | Medium for labels, High for unreachable elements |
166
+ | `[UX]` | User Experience | Dead buttons, confusing navigation, unexpected behavior | High for dead buttons, Medium for confusing flows |
167
+ | `[CONTENT]` | Content | Template leftovers, placeholder text, wrong domain text | High for template leftovers, Medium for placeholders |
168
+ | `[STATE]` | State | State not updating, data not persisting, stale views | High for data loss, Medium for stale views |
169
+ | `[CRASH]` | Crash | App crashes on interaction | Critical (P0) |
170
+
171
+ Format each bug as:
172
+
173
+ ```markdown
174
+ ### BUG-{NNN}: [{SEVERITY}][EXPLORATORY][{TAG}] {Short description}
175
+ - **Screen:** {screen name}
176
+ - **Element:** {element description from a11y tree}
177
+ - **Steps to reproduce:** {what you tapped/typed/swiped}
178
+ - **Expected:** {what should happen}
179
+ - **Actual:** {what actually happened}
180
+ - **Screenshot context:** {brief description of what the screenshot showed}
181
+ ```
182
+
183
+ ### Phase D — Write Bug Report Section
184
+
185
+ Append your findings to `output/{app-name}/reports/11-bug-report.md`. If the file doesn't exist, create it. Use this structure:
186
+
187
+ ```markdown
188
+ # Bug Report — Exploratory Testing
189
+
190
+ ## Summary
191
+ - Screens discovered: {N}
192
+ - Screens audited: {N}
193
+ - Total interactive elements found: {N}
194
+ - Total interactions performed: {N}
195
+ - Bugs found: {N}
196
+ - Critical (P0): {N}
197
+ - High (P1): {N}
198
+ - Medium (P2): {N}
199
+ - Low (P3): {N}
200
+
201
+ ## Accessibility Summary
202
+ - Elements with labels: {N}/{total}
203
+ - Elements missing labels: {N}
204
+ - Images with descriptions: {N}/{total}
205
+ - Touch targets below minimum: {N}
206
+
207
+ ## Screen Map
208
+ {screen discovery tree from Phase A}
209
+
210
+ ## Bugs
211
+ {all bugs from Phase C}
212
+ ```
213
+
214
+ ## Important Rules
215
+
216
+ - **DO NOT read source code** — your inputs are ONLY screenshots and the element tree from `inspect()`
217
+ - **DO NOT follow a test plan** — explore freely based on what you see
218
+ - **Test EVERY interactive element** you discover in the element tree
219
+ - Use ai-tester MCP tools (`inspect`, `act`, `assert`, `wait`) for ALL interaction — NOT shell commands
220
+ - If the app crashes, document it as a P0/Critical bug and try to restart
221
+ - You are testing on a SINGLE assigned simulator — use the provided `deviceName` in all MCP calls
222
+ - Write your findings to the bug report — do NOT fix bugs (that's the bug-fixer skill's job)
223
+ - Be thorough: an element tree with 20 buttons means you tap all 20 buttons
@@ -9,7 +9,7 @@ Use this skill to perform deep iOS/SwiftUI customization on a cloned template.
9
9
 
10
10
  ## When to Use
11
11
 
12
- - At Step 8 of the AppAgent workflow, when the platform is iOS
12
+ - At Step 5 of the AppAgent workflow, when the platform is iOS
13
13
  - After AppConfig, mock data, and design tokens are already in place
14
14
  - When you need to modify SwiftUI views, navigation, and themes
15
15
 
@@ -100,7 +100,21 @@ iOS templates use a `Colors.swift` theme file:
100
100
  - Use semantic colors where possible (`Color.primary`, `Color.secondary`)
101
101
  - Apply corner radius and spacing tokens from the design brief
102
102
 
103
- ### Step 4.5 — Generate App Launcher Icon
103
+ ### Step 4.5 — Visual Polish Application
104
+
105
+ Apply the **Visual Polish Plan** from the customization manifest. This step is MANDATORY for every build — it makes apps feel handcrafted rather than template-like.
106
+
107
+ 1. **Shadows**: Apply `.cardShadow()` to every card, list item card, and elevated surface. Use `.elevatedShadow()` for FABs and floating buttons. If no `.cardShadow()` modifier exists, add `shadow(color:radius:x:y:)` directly using the manifest's shadow tokens.
108
+ 2. **Gradients**: Apply `LinearGradient` to hero sections (detail screen headers, banner areas) and primary CTA buttons per the manifest's gradient usage plan. Use `Color.appPrimary` → `Color.appSecondary` for primary gradient.
109
+ 3. **Press feedback**: Ensure all `Button` and tappable elements use `.scaleOnPress()` or `ButtonStyle` with scale animation (scale to 0.95 on press, spring return). For key interactions (favorite toggle, submit), add `UIImpactFeedbackGenerator(style: .light).impactOccurred()`.
110
+ 4. **Staggered animations**: Ensure all `ForEach` / `LazyVStack` list views apply `.staggeredAppear(index:)` to each item. This gives lists a lively cascading entrance.
111
+ 5. **Empty states**: Enhance empty state views — add a gradient circle background behind the icon (e.g., `Circle().fill(LinearGradient(...)).frame(width: 80, height: 80)` behind the SF Symbol), and add `.slideIn()` animation to the entire empty state composition.
112
+ 6. **Glass/blur**: Apply `.ultraThinMaterial` or `.regularMaterial` to sheet backgrounds and overlay views. Use `.background(.ultraThinMaterial)` on navigation bars where appropriate.
113
+ 7. **Haptic feedback**: Add `UIImpactFeedbackGenerator` calls to favorite toggles, pull-to-refresh completions, and primary action buttons.
114
+
115
+ Verify visual hierarchy: hero elements (large images, gradients) stand out > section headers > cards (with shadows) > inline text.
116
+
117
+ ### Step 4.6 — Generate App Launcher Icon
104
118
 
105
119
  Generate a domain-specific app icon using the manifest's **App Launcher Icon** section:
106
120
 
@@ -229,26 +243,47 @@ If the customization manifest includes an **"Animation & Sensory Plan"** section
229
243
  3. **Apply the modifiers** using the imported module code:
230
244
 
231
245
  ```swift
232
- // MicroInteractions press feedback
246
+ // Built-in press feedback (always available)
233
247
  Button("Add to Cart") { ... }
234
248
  .scaleOnPress()
235
- .hapticFeedback(.impact(.medium))
249
+ .hapticFeedback(.medium)
250
+
251
+ // Built-in staggered list items (always available)
252
+ ForEach(items.indices, id: \.self) { index in
253
+ ItemRow(item: items[index])
254
+ .staggeredAppear(index: index)
255
+ }
256
+
257
+ // Built-in shadow (always available)
258
+ CardView(item: item)
259
+ .cardShadow()
236
260
 
237
- // ScrollEffects parallax header
261
+ // Built-in slide animation (always available)
262
+ EmptyStateView()
263
+ .slideIn()
264
+
265
+ // Built-in shimmer for loading placeholders (always available)
266
+ RoundedRectangle(cornerRadius: 8)
267
+ .fill(Color(.systemGray5))
268
+ .frame(height: 60)
269
+ .shimmer()
270
+
271
+ // Built-in pulse for live indicators (always available)
272
+ Circle().fill(.red).frame(width: 8, height: 8)
273
+ .pulse()
274
+
275
+ // Built-in heart bounce for favorites (always available)
276
+ Image(systemName: isFavorite ? "heart.fill" : "heart")
277
+ .heartBounce(isActive: isFavorite)
278
+
279
+ // ScrollEffects module — parallax header (if module integrated)
238
280
  ScrollView {
239
281
  ParallaxHeaderView(height: 280) {
240
282
  AsyncImage(url: item.imageURL)
241
283
  }
242
- // content below
243
- }
244
-
245
- // AnimatedTransitions — staggered list items
246
- ForEach(items.indices, id: \.self) { index in
247
- ItemRow(item: items[index])
248
- .staggeredAppearance(index: index)
249
284
  }
250
285
 
251
- // CelebrationEffects — success overlay
286
+ // CelebrationEffects module — success overlay (if module integrated)
252
287
  .celebrationOverlay(isPresented: $showCelebration, style: .confetti)
253
288
  ```
254
289
 
@@ -262,7 +297,7 @@ Write the report to `output/{app-name}/reports/05-customization.md` (append iOS
262
297
  ```markdown
263
298
  # Customization Report
264
299
 
265
- **Step:** 8
300
+ **Step:** 5
266
301
  **Skill:** ios-customizer
267
302
  **Timestamp:** {ISO 8601}
268
303
  **Result:** PASS
@@ -312,7 +347,7 @@ Update `output/{app-name}/reports/summary.json` — read the file, append this s
312
347
 
313
348
  ```json
314
349
  {
315
- "step": 8,
350
+ "step": 5,
316
351
  "name": "ios-customization",
317
352
  "startedAt": "{ISO 8601 timestamp}",
318
353
  "durationSeconds": 0,
@@ -9,7 +9,7 @@ Integrate selected shared modules into the app by reading their reference implem
9
9
 
10
10
  ## Context You Need
11
11
 
12
- - **Module selection report** — `output/{app-name}/reports/02b-module-selection.md` — lists which modules were selected
12
+ - **Catalog analysis report** — `output/{app-name}/reports/02-catalog-analysis.md` — lists which template and modules were selected
13
13
  - **Customization manifest** — `output/{app-name}/reports/customization-manifest.md` — contains the module integration plan with placement decisions
14
14
  - **Platform** — `ios` or `android`
15
15
  - **App name and bundle ID** — for adapting imports and identifiers
@@ -19,7 +19,7 @@ Integrate selected shared modules into the app by reading their reference implem
19
19
 
20
20
  ### Step 1 — Read the Module Selection Report
21
21
 
22
- Read `output/{app-name}/reports/02b-module-selection.md` to get the list of selected modules. Read the "Module Integration Plan" section from the customization manifest to understand where each module connects.
22
+ Read the "Module Selection" section from `output/{app-name}/reports/02-catalog-analysis.md` to get the list of selected modules. Read the "Module Integration Plan" section from the customization manifest to understand where each module connects.
23
23
 
24
24
  ### Step 2 — For Each Selected Module, Integrate It
25
25
 
@@ -101,7 +101,7 @@ Mark all animation issues as `[WARN]` — they affect quality but are not build-
101
101
 
102
102
  Verify that the reports directory has the expected files:
103
103
  - `01-prompt-validation.md` exists
104
- - `02-template-selection.md` exists
104
+ - `02-catalog-analysis.md` exists
105
105
  - `03-design-brand.md` exists
106
106
  - `04-content-brief.md` exists
107
107
  - `05-customization.md` exists
@@ -133,12 +133,29 @@ const lightColors = {
133
133
  };
134
134
  ```
135
135
 
136
- ### Step 5 — Data Layer & Mock Data
136
+ ### Step 4.5 — Visual Polish Application
137
137
 
138
- Templates use a `MockDataProvider.ts` that implements `DataRepository` interface:
138
+ Apply the **Visual Polish Plan** from the customization manifest. This step is MANDATORY — it makes apps feel handcrafted rather than template-like.
139
+
140
+ 1. **Shadows**: Apply shadow styles to every card and elevated surface. Use platform-aware styles: `shadowColor`, `shadowOffset`, `shadowOpacity`, `shadowRadius` (iOS) and `elevation` (Android). Create a `cardShadow` style object and spread it into card `StyleSheet` definitions.
141
+ 2. **Gradients**: Use `expo-linear-gradient` `LinearGradient` component for hero sections (detail screen headers, banner areas) and primary CTA buttons. Apply `colors={[colors.primary, colors.secondary]}` with `start/end` props for direction.
142
+ 3. **Press feedback**: Ensure all `TouchableOpacity` elements use `activeOpacity={0.85}` and add a scale animation on press using `Animated.spring()` (scale to 0.95, spring back). Wrap common buttons in a `ScaleButton` component.
143
+ 4. **Staggered animations**: Ensure all `FlatList` items use `useStaggeredAppear(index)` hook for animated entrance. Apply the returned `animatedStyle` to each item's `Animated.View` wrapper.
144
+ 5. **Empty states**: Enhance empty state components — add a gradient circle background behind the icon (using `LinearGradient` + `borderRadius: 40`), and add slide-in animation with `useSlideIn()`.
145
+ 6. **Blur/glass**: Use `expo-blur` `BlurView` for modal/sheet backgrounds where supported. Fall back to semi-transparent background.
146
+ 7. **Haptic feedback**: Use `expo-haptics` or React Native `Vibration` API on favorite toggles and primary actions.
147
+
148
+ Verify visual hierarchy: hero elements > section headers > cards (with shadows) > inline text.
149
+
150
+ ### Step 5 — Data Layer & Offline Infrastructure
151
+
152
+ Templates include an **offline-first data layer**. Understand these components:
139
153
 
140
154
  **Files you should NOT modify** (generic infrastructure):
141
155
  - `src/data/DataRepository.ts` — interface definition
156
+ - `src/data/NetworkMonitor.ts` — connectivity detection
157
+ - `src/data/LocalPersistence.ts` — JSON file persistence
158
+ - `src/components/OfflineBanner.tsx` — offline status banner
142
159
  - `src/theme/ThemeContext.tsx` — theme provider
143
160
 
144
161
  **Files you MAY need to update** (domain-specific):
@@ -147,25 +164,29 @@ Templates use a `MockDataProvider.ts` that implements `DataRepository` interface
147
164
  - All CRUD methods work with correct entity types
148
165
  - Subscription/onChange pattern is working
149
166
  - Reset method references correct defaults
167
+ - `src/data/SyncManager.ts` — if entity types were renamed, update domain methods
150
168
 
151
169
  **ViewModel patterns** (how screens consume data):
152
170
 
153
- ViewModels are custom React hooks that use MockDataProvider:
171
+ ViewModels are custom React hooks that use the `DataRepository` interface via `DataSourceResolver`, not `MockDataProvider` directly:
154
172
  ```tsx
173
+ import { DataSourceResolver } from '../data/DataSourceResolver';
174
+
155
175
  export function useHomeViewModel() {
176
+ const repository = DataSourceResolver.repository;
156
177
  const [items, setItems] = useState<Item[]>([]);
157
178
  const [isLoading, setIsLoading] = useState(true);
158
179
 
159
180
  useEffect(() => {
160
181
  loadData();
161
- const unsubscribe = MockDataProvider.subscribe(() => {
162
- setItems(MockDataProvider.getItems());
182
+ const unsubscribe = repository.subscribe(() => {
183
+ setItems(repository.getItems());
163
184
  });
164
185
  return unsubscribe;
165
186
  }, []);
166
187
 
167
188
  const loadData = () => {
168
- setItems(MockDataProvider.getItems());
189
+ setItems(repository.getItems());
169
190
  setIsLoading(false);
170
191
  };
171
192
 
@@ -173,6 +194,16 @@ export function useHomeViewModel() {
173
194
  }
174
195
  ```
175
196
 
197
+ When customizing ViewModels, ALWAYS use `DataSourceResolver.repository`, never `MockDataProvider` directly. This ensures the app works with any data source (mock, localStorage, Firebase, Supabase, custom API).
198
+
199
+ **OfflineBanner integration** — add to main navigation screens:
200
+ ```tsx
201
+ <View style={{ flex: 1 }}>
202
+ <OfflineBanner />
203
+ {/* ... rest of screen content */}
204
+ </View>
205
+ ```
206
+
176
207
  ### Step 6 — Data Layer Verification
177
208
 
178
209
  Verify the data layer uses correct patterns:
@@ -193,9 +224,36 @@ export interface DataRepository {
193
224
  }
194
225
  ```
195
226
 
227
+ **MockDataProvider (implements DataRepository):**
228
+ ```tsx
229
+ class MockDataProvider implements DataRepository {
230
+ private static instance: MockDataProvider;
231
+ private listeners: Set<() => void> = new Set();
232
+
233
+ getItems(): Item[] { ... }
234
+ addItem(item: Item): void { ... }
235
+ subscribe(callback: () => void): () => void { ... }
236
+ reset(): void { ... } // Reset to defaults
237
+ }
238
+ ```
239
+
240
+ **DataSourceResolver:**
241
+ ```tsx
242
+ export class DataSourceResolver {
243
+ static get repository(): DataRepository {
244
+ switch (AppConfig.DataSource.active) {
245
+ case 'localStorage': return MockDataProvider.getInstance(); // TODO: LocalStorageProvider
246
+ case 'mock': return MockDataProvider.getInstance();
247
+ }
248
+ }
249
+ }
250
+ ```
251
+
196
252
  Verify:
197
- - MockDataProvider implements all DataRepository methods
198
- - ViewModel hooks use MockDataProvider correctly
253
+ - DataRepository interface matches MockDataProvider's public methods
254
+ - MockDataProvider implements DataRepository
255
+ - DataSourceResolver returns the correct implementation
256
+ - ViewModel hooks use `DataSourceResolver.repository`, not `MockDataProvider` directly
199
257
  - Seed data matches the new domain
200
258
  - CRUD methods reference correct entity types
201
259
 
@@ -208,35 +266,59 @@ If the customization manifest includes an **"Animation & Sensory Plan"** section
208
266
  3. **Apply the modifiers** using the imported module hooks/components:
209
267
 
210
268
  ```typescript
211
- // MicroInteractions press feedback
212
- import { useScaleOnPress, useStaggeredAppearance } from '../shared/MicroInteractions';
213
- import { useHapticEngine } from '../shared/HapticEngine';
214
-
215
- const { animatedStyle, handlers } = useScaleOnPress();
216
- const haptic = useHapticEngine();
269
+ // Built-in hooks (always available in animation/useAnimatedList)
270
+ import { useScaleOnPress, useStaggeredAppear, useSlideIn, cardShadowStyle, useShimmer, usePulse, useHeartBounce } from '../animation/useAnimatedList';
217
271
 
218
- <Animated.View style={animatedStyle} {...handlers}>
219
- <TouchableOpacity onPress={() => { haptic.impact('medium'); doAction(); }}>
272
+ // Press feedback
273
+ const { style: pressStyle, onPressIn, onPressOut } = useScaleOnPress();
274
+ <Animated.View style={pressStyle}>
275
+ <Pressable onPressIn={onPressIn} onPressOut={onPressOut} onPress={doAction}>
220
276
  <Text>Add to Cart</Text>
221
- </TouchableOpacity>
277
+ </Pressable>
222
278
  </Animated.View>
223
279
 
224
- // ScrollEffects parallax header
225
- import { ParallaxHeader } from '../shared/ScrollEffects';
280
+ // Staggered list items
281
+ const animStyle = useStaggeredAppear(index);
282
+ <Animated.View style={animStyle}><ItemCard item={item} /></Animated.View>
226
283
 
227
- <ParallaxHeader height={280} imageSource={item.imageUrl}>
228
- {/* content below */}
229
- </ParallaxHeader>
284
+ // Card shadow
285
+ <View style={[styles.card, cardShadowStyle()]}>{/* content */}</View>
230
286
 
231
- // CelebrationEffects — success overlay
232
- import { CelebrationOverlay } from '../shared/CelebrationEffects';
287
+ // Slide-in animation
288
+ const slideStyle = useSlideIn();
289
+ <Animated.View style={slideStyle}><EmptyState /></Animated.View>
233
290
 
291
+ // Shimmer for loading placeholders
292
+ const shimmerStyle = useShimmer();
293
+ <Animated.View style={[styles.placeholder, shimmerStyle]} />
294
+
295
+ // Pulse for live indicators
296
+ const pulseStyle = usePulse();
297
+ <Animated.View style={[styles.dot, pulseStyle]} />
298
+
299
+ // Heart bounce for favorite toggles
300
+ const heartStyle = useHeartBounce(isFavorite);
301
+ <Animated.View style={heartStyle}><HeartIcon /></Animated.View>
302
+
303
+ // Modules (if integrated)
304
+ import { ParallaxHeader } from '../shared/ScrollEffects';
305
+ <ParallaxHeader height={280} imageSource={item.imageUrl} />
306
+
307
+ import { CelebrationOverlay } from '../shared/CelebrationEffects';
234
308
  <CelebrationOverlay isActive={showCelebration} style="confetti" />
235
309
  ```
236
310
 
237
311
  4. **Wrap all applied animations** with `useMotionPreferences` checks if MotionPreferences is integrated
238
312
  5. **Do NOT add animation modifiers** to screens not listed in the manifest
239
313
 
314
+ ### Step 6.7 — App Icon (if applicable)
315
+
316
+ React Native apps target both iOS and Android. App icon customization is handled per-platform:
317
+ - **iOS**: If `scripts/generate-app-icon.swift` exists, run it with the SF Symbol and background color from the manifest
318
+ - **Android**: If `templates/android/ICON_VECTORS.md` exists, update `android/app/src/main/res/drawable/ic_launcher_foreground.xml` and `ic_launcher_background` color
319
+
320
+ If the scripts/resources are not available, skip — the template's default icons will be used.
321
+
240
322
  ## Report Output
241
323
 
242
324
  Write the report to `output/{app-name}/reports/05-customization.md`:
@@ -269,6 +351,12 @@ React Native (TypeScript)
269
351
  - Empty states: {count}
270
352
  - CTAs updated: {count}
271
353
 
354
+ ## Offline Infrastructure
355
+
356
+ - OfflineBanner integrated: {yes/no}
357
+ - SyncManager methods updated: {list or "no changes needed"}
358
+ - Data persistence verified: {yes/no}
359
+
272
360
  ## Animation Modifiers Applied
273
361
 
274
362
  - {modifier}: applied to {screen} — {description}
@@ -324,8 +412,10 @@ Templates include `testID` props on all interactive elements and a `TESTING_MANI
324
412
  - ONLY edit files under `output/` — never touch `templates/`
325
413
  - Ensure all imports resolve after your changes
326
414
  - Preserve `MockDataProvider` CRUD methods and `reset()` function
415
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
416
+ - Ensure OfflineBanner is integrated into main navigation screens
327
417
  - Use `testID` props on ALL interactive elements
328
- - Follow `{screen}_{element}_{role}` convention for new test IDs
418
+ - Follow `{screen}_{element}_{role}` convention for new test IDs (see `templates/TEST_ID_CONVENTIONS.md`)
329
419
  - Use `StyleSheet.create()` for styles, not inline objects
330
420
  - Use functional components with TypeScript
331
421
  - Keep React Navigation structure intact