@buivietphi/skill-mobile-mt 2.0.1 → 2.2.0
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.
- package/AGENTS.md +96 -40
- package/README.md +77 -40
- package/SKILL.md +762 -54
- package/package.json +1 -1
- package/shared/bug-detection.md +411 -27
- package/shared/code-generation-templates.md +656 -0
- package/shared/code-review.md +899 -37
- package/shared/complex-ui-patterns.md +526 -0
- package/shared/data-flow-patterns.md +422 -0
- package/shared/debugging-intelligence.md +787 -0
- package/shared/error-handling.md +394 -0
- package/shared/i18n-localization.md +426 -0
- package/shared/intent-analysis.md +473 -0
- package/shared/navigation-patterns.md +375 -0
- package/shared/prompt-engineering.md +176 -20
- package/shared/spec-to-code.md +293 -0
- package/shared/storage-patterns.md +312 -0
- package/shared/testing-patterns.md +428 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# Navigation Patterns — Complex Flows
|
|
2
|
+
|
|
3
|
+
> On-demand module. Loaded when implementing auth flows, deep links, modals, or tab navigation.
|
|
4
|
+
> Contains production patterns for React Native, Flutter, iOS, and Android.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Auth-Based Navigation Flow
|
|
9
|
+
|
|
10
|
+
### React Native (React Navigation)
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
// navigation/RootNavigator.tsx
|
|
14
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
15
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
16
|
+
import { useAuthStore, useIsLoggedIn } from '@/stores/useAuthStore';
|
|
17
|
+
|
|
18
|
+
const Stack = createNativeStackNavigator<RootStackParamList>();
|
|
19
|
+
|
|
20
|
+
export function RootNavigator() {
|
|
21
|
+
const isLoggedIn = useIsLoggedIn();
|
|
22
|
+
const [isReady, setIsReady] = useState(false);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
// Check stored token on app start
|
|
26
|
+
async function bootstrap() {
|
|
27
|
+
const hasToken = useAuthStore.getState().token;
|
|
28
|
+
if (hasToken) {
|
|
29
|
+
const valid = await useAuthStore.getState().refreshSession();
|
|
30
|
+
if (!valid) useAuthStore.getState().logout();
|
|
31
|
+
}
|
|
32
|
+
setIsReady(true);
|
|
33
|
+
}
|
|
34
|
+
bootstrap();
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
if (!isReady) return <SplashScreen />;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<NavigationContainer>
|
|
41
|
+
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
|
42
|
+
{isLoggedIn ? (
|
|
43
|
+
// Authenticated stack
|
|
44
|
+
<>
|
|
45
|
+
<Stack.Screen name="MainTabs" component={MainTabNavigator} />
|
|
46
|
+
<Stack.Screen name="ProductDetail" component={ProductDetailScreen} />
|
|
47
|
+
<Stack.Screen name="Settings" component={SettingsScreen} />
|
|
48
|
+
{/* Modals */}
|
|
49
|
+
<Stack.Group screenOptions={{ presentation: 'modal' }}>
|
|
50
|
+
<Stack.Screen name="EditProfile" component={EditProfileScreen} />
|
|
51
|
+
<Stack.Screen name="ImageViewer" component={ImageViewerScreen} />
|
|
52
|
+
</Stack.Group>
|
|
53
|
+
</>
|
|
54
|
+
) : (
|
|
55
|
+
// Unauthenticated stack
|
|
56
|
+
<>
|
|
57
|
+
<Stack.Screen name="Onboarding" component={OnboardingScreen} />
|
|
58
|
+
<Stack.Screen name="Login" component={LoginScreen} />
|
|
59
|
+
<Stack.Screen name="Register" component={RegisterScreen} />
|
|
60
|
+
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
</Stack.Navigator>
|
|
64
|
+
</NavigationContainer>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Flutter (GoRouter)
|
|
70
|
+
|
|
71
|
+
```dart
|
|
72
|
+
// navigation/app_router.dart
|
|
73
|
+
import 'package:go_router/go_router.dart';
|
|
74
|
+
|
|
75
|
+
final appRouter = GoRouter(
|
|
76
|
+
initialLocation: '/',
|
|
77
|
+
redirect: (context, state) {
|
|
78
|
+
final isLoggedIn = ref.read(authProvider).isLoggedIn;
|
|
79
|
+
final isAuthRoute = state.matchedLocation.startsWith('/auth');
|
|
80
|
+
|
|
81
|
+
if (!isLoggedIn && !isAuthRoute) return '/auth/login';
|
|
82
|
+
if (isLoggedIn && isAuthRoute) return '/';
|
|
83
|
+
return null;
|
|
84
|
+
},
|
|
85
|
+
routes: [
|
|
86
|
+
// Auth routes
|
|
87
|
+
GoRoute(path: '/auth/login', builder: (_, __) => const LoginScreen()),
|
|
88
|
+
GoRoute(path: '/auth/register', builder: (_, __) => const RegisterScreen()),
|
|
89
|
+
|
|
90
|
+
// App routes with bottom nav shell
|
|
91
|
+
ShellRoute(
|
|
92
|
+
builder: (_, __, child) => ScaffoldWithNavBar(child: child),
|
|
93
|
+
routes: [
|
|
94
|
+
GoRoute(path: '/', builder: (_, __) => const HomeScreen()),
|
|
95
|
+
GoRoute(path: '/search', builder: (_, __) => const SearchScreen()),
|
|
96
|
+
GoRoute(path: '/cart', builder: (_, __) => const CartScreen()),
|
|
97
|
+
GoRoute(path: '/profile', builder: (_, __) => const ProfileScreen()),
|
|
98
|
+
],
|
|
99
|
+
),
|
|
100
|
+
|
|
101
|
+
// Detail routes (no bottom nav)
|
|
102
|
+
GoRoute(
|
|
103
|
+
path: '/product/:id',
|
|
104
|
+
builder: (_, state) => ProductDetailScreen(id: state.pathParameters['id']!),
|
|
105
|
+
),
|
|
106
|
+
],
|
|
107
|
+
);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Deep Linking
|
|
113
|
+
|
|
114
|
+
### React Native (Expo Router)
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// app/_layout.tsx — Expo Router handles deep links automatically via file structure
|
|
118
|
+
// URL: myapp://product/abc-123 → app/product/[id].tsx
|
|
119
|
+
|
|
120
|
+
// app/product/[id].tsx
|
|
121
|
+
import { useLocalSearchParams } from 'expo-router';
|
|
122
|
+
|
|
123
|
+
export default function ProductDetailScreen() {
|
|
124
|
+
const { id } = useLocalSearchParams<{ id: string }>();
|
|
125
|
+
|
|
126
|
+
// Validate param exists
|
|
127
|
+
if (!id) return <NotFoundScreen />;
|
|
128
|
+
|
|
129
|
+
// Fetch and render
|
|
130
|
+
const { data, isLoading } = useProductDetail(id as ProductId);
|
|
131
|
+
// ...
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### React Navigation Deep Link Config
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// navigation/linking.ts
|
|
139
|
+
const linking: LinkingOptions<RootStackParamList> = {
|
|
140
|
+
prefixes: ['myapp://', 'https://myapp.com'],
|
|
141
|
+
config: {
|
|
142
|
+
screens: {
|
|
143
|
+
MainTabs: {
|
|
144
|
+
screens: {
|
|
145
|
+
Home: 'home',
|
|
146
|
+
Profile: 'profile/:userId',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
ProductDetail: 'product/:productId',
|
|
150
|
+
Settings: 'settings',
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Handle notification deep links
|
|
156
|
+
import * as Notifications from 'expo-notifications';
|
|
157
|
+
|
|
158
|
+
function useNotificationDeepLink() {
|
|
159
|
+
const navigation = useAppNavigation();
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
const sub = Notifications.addNotificationResponseReceivedListener(response => {
|
|
163
|
+
const data = response.notification.request.content.data;
|
|
164
|
+
if (data.screen === 'ProductDetail' && data.productId) {
|
|
165
|
+
navigation.navigate('ProductDetail', { productId: data.productId as ProductId });
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return () => sub.remove();
|
|
169
|
+
}, [navigation]);
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Bottom Tab Navigation
|
|
176
|
+
|
|
177
|
+
### React Native — Lazy Tabs with State Preservation
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// navigation/MainTabNavigator.tsx
|
|
181
|
+
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
|
182
|
+
|
|
183
|
+
const Tab = createBottomTabNavigator<MainTabParamList>();
|
|
184
|
+
|
|
185
|
+
export function MainTabNavigator() {
|
|
186
|
+
return (
|
|
187
|
+
<Tab.Navigator
|
|
188
|
+
screenOptions={{
|
|
189
|
+
headerShown: false,
|
|
190
|
+
// Lazy load: render tab only when first visited
|
|
191
|
+
lazy: true,
|
|
192
|
+
// Freeze inactive tabs (prevent re-renders)
|
|
193
|
+
freezeOnBlur: true,
|
|
194
|
+
tabBarActiveTintColor: theme.colors.primary,
|
|
195
|
+
}}
|
|
196
|
+
>
|
|
197
|
+
<Tab.Screen
|
|
198
|
+
name="Home"
|
|
199
|
+
component={HomeScreen}
|
|
200
|
+
options={{
|
|
201
|
+
tabBarIcon: ({ color, size }) => <HomeIcon color={color} size={size} />,
|
|
202
|
+
tabBarLabel: 'Home',
|
|
203
|
+
}}
|
|
204
|
+
/>
|
|
205
|
+
<Tab.Screen
|
|
206
|
+
name="Search"
|
|
207
|
+
component={SearchScreen}
|
|
208
|
+
options={{
|
|
209
|
+
tabBarIcon: ({ color, size }) => <SearchIcon color={color} size={size} />,
|
|
210
|
+
}}
|
|
211
|
+
/>
|
|
212
|
+
<Tab.Screen
|
|
213
|
+
name="Cart"
|
|
214
|
+
component={CartScreen}
|
|
215
|
+
options={{
|
|
216
|
+
tabBarIcon: ({ color, size }) => <CartIcon color={color} size={size} />,
|
|
217
|
+
tabBarBadge: cartCount > 0 ? cartCount : undefined,
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
<Tab.Screen
|
|
221
|
+
name="Profile"
|
|
222
|
+
component={ProfileScreen}
|
|
223
|
+
options={{
|
|
224
|
+
tabBarIcon: ({ color, size }) => <ProfileIcon color={color} size={size} />,
|
|
225
|
+
}}
|
|
226
|
+
/>
|
|
227
|
+
</Tab.Navigator>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Modal Navigation
|
|
235
|
+
|
|
236
|
+
### React Native — Modal Stack
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Present as modal (slides up from bottom on iOS)
|
|
240
|
+
navigation.navigate('EditProfile'); // registered in modal group
|
|
241
|
+
|
|
242
|
+
// Dismiss modal
|
|
243
|
+
navigation.goBack();
|
|
244
|
+
|
|
245
|
+
// Modal with result — pass callback via params or use event
|
|
246
|
+
// Option A: Use navigation params
|
|
247
|
+
navigation.navigate('SelectAddress', {
|
|
248
|
+
onSelect: (address: Address) => {
|
|
249
|
+
// handle selected address
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Option B: Use event emitter
|
|
254
|
+
import { DeviceEventEmitter } from 'react-native';
|
|
255
|
+
// In modal: DeviceEventEmitter.emit('addressSelected', address);
|
|
256
|
+
// In parent: DeviceEventEmitter.addListener('addressSelected', handler);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Flutter — Modal Bottom Sheet
|
|
260
|
+
|
|
261
|
+
```dart
|
|
262
|
+
// Modal bottom sheet
|
|
263
|
+
showModalBottomSheet(
|
|
264
|
+
context: context,
|
|
265
|
+
isScrollControlled: true, // full-height if needed
|
|
266
|
+
useSafeArea: true,
|
|
267
|
+
builder: (context) => DraggableScrollableSheet(
|
|
268
|
+
initialChildSize: 0.6,
|
|
269
|
+
minChildSize: 0.3,
|
|
270
|
+
maxChildSize: 0.9,
|
|
271
|
+
builder: (_, controller) => AddressPickerSheet(scrollController: controller),
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Push Notification Navigation
|
|
279
|
+
|
|
280
|
+
### React Native (Expo Notifications)
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// hooks/useNotificationSetup.ts
|
|
284
|
+
import * as Notifications from 'expo-notifications';
|
|
285
|
+
import * as Device from 'expo-device';
|
|
286
|
+
|
|
287
|
+
export function useNotificationSetup() {
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
registerForPush();
|
|
290
|
+
}, []);
|
|
291
|
+
|
|
292
|
+
async function registerForPush() {
|
|
293
|
+
if (!Device.isDevice) return; // skip simulator
|
|
294
|
+
|
|
295
|
+
const { status } = await Notifications.getPermissionsAsync();
|
|
296
|
+
let finalStatus = status;
|
|
297
|
+
|
|
298
|
+
if (status !== 'granted') {
|
|
299
|
+
const { status: newStatus } = await Notifications.requestPermissionsAsync();
|
|
300
|
+
finalStatus = newStatus;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (finalStatus !== 'granted') return;
|
|
304
|
+
|
|
305
|
+
const token = (await Notifications.getExpoPushTokenAsync()).data;
|
|
306
|
+
await userService.registerPushToken(token);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Notification handler — runs when app receives notification
|
|
311
|
+
Notifications.setNotificationHandler({
|
|
312
|
+
handleNotification: async () => ({
|
|
313
|
+
shouldShowAlert: true,
|
|
314
|
+
shouldPlaySound: true,
|
|
315
|
+
shouldSetBadge: true,
|
|
316
|
+
}),
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Permissions Handling Pattern
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
// hooks/usePermission.ts
|
|
326
|
+
import * as Location from 'expo-location';
|
|
327
|
+
import * as Camera from 'expo-camera';
|
|
328
|
+
import { Alert, Linking } from 'react-native';
|
|
329
|
+
|
|
330
|
+
type PermissionType = 'camera' | 'location' | 'notifications';
|
|
331
|
+
|
|
332
|
+
export function usePermission(type: PermissionType) {
|
|
333
|
+
const [granted, setGranted] = useState<boolean | null>(null);
|
|
334
|
+
|
|
335
|
+
const request = useCallback(async () => {
|
|
336
|
+
let result: { status: string };
|
|
337
|
+
|
|
338
|
+
switch (type) {
|
|
339
|
+
case 'camera':
|
|
340
|
+
result = await Camera.requestCameraPermissionsAsync();
|
|
341
|
+
break;
|
|
342
|
+
case 'location':
|
|
343
|
+
result = await Location.requestForegroundPermissionsAsync();
|
|
344
|
+
break;
|
|
345
|
+
case 'notifications':
|
|
346
|
+
result = await Notifications.requestPermissionsAsync();
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (result.status === 'granted') {
|
|
351
|
+
setGranted(true);
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Permission denied — guide user to settings
|
|
356
|
+
Alert.alert(
|
|
357
|
+
'Permission Required',
|
|
358
|
+
`Please enable ${type} access in Settings to use this feature.`,
|
|
359
|
+
[
|
|
360
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
361
|
+
{ text: 'Open Settings', onPress: () => Linking.openSettings() },
|
|
362
|
+
]
|
|
363
|
+
);
|
|
364
|
+
setGranted(false);
|
|
365
|
+
return false;
|
|
366
|
+
}, [type]);
|
|
367
|
+
|
|
368
|
+
return { granted, request };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Usage:
|
|
372
|
+
// const camera = usePermission('camera');
|
|
373
|
+
// const canUse = await camera.request();
|
|
374
|
+
// if (canUse) { /* proceed */ }
|
|
375
|
+
```
|
|
@@ -64,40 +64,138 @@
|
|
|
64
64
|
```xml
|
|
65
65
|
<think>
|
|
66
66
|
BUG: [description]
|
|
67
|
-
|
|
67
|
+
ERROR MESSAGE: [paste exact error]
|
|
68
|
+
|
|
69
|
+
⛔ STOP — CLASSIFY + SEARCH PROJECT FIRST (before ANY analysis):
|
|
70
|
+
|
|
71
|
+
<error_classification>
|
|
72
|
+
TYPE: [RUNTIME CRASH / BUILD ERROR / TYPE MISMATCH / NETWORK ERROR /
|
|
73
|
+
RENDER ERROR / NAVIGATION ERROR / PERFORMANCE / STATE ERROR /
|
|
74
|
+
NATIVE ERROR / MEMORY ERROR / INVESTIGATION]
|
|
75
|
+
|
|
76
|
+
SEARCH STRATEGY based on type:
|
|
77
|
+
→ RUNTIME/STATE/RENDER/NAVIGATION → Search src/ FIRST → then trace outward
|
|
78
|
+
→ BUILD/NATIVE → Search config files FIRST (tsconfig/gradle/Pod/pubspec)
|
|
79
|
+
→ NETWORK/API → Search API service files → then .env → then interceptors
|
|
80
|
+
→ INVESTIGATION → Search by feature name → read → report (don't fix yet)
|
|
81
|
+
|
|
82
|
+
If complex bug → Read shared/debugging-intelligence.md for pattern match
|
|
83
|
+
</error_classification>
|
|
84
|
+
|
|
85
|
+
<project_search>
|
|
86
|
+
STEP 1: Extract keywords from error:
|
|
87
|
+
→ File/path in stack trace: [extract]
|
|
88
|
+
→ Function/class/component name: [extract]
|
|
89
|
+
→ Module/package name: [extract]
|
|
90
|
+
→ Line number: [extract if available]
|
|
91
|
+
→ Error code / HTTP status: [extract if available]
|
|
92
|
+
|
|
93
|
+
STEP 2: Filter noise from log (if user pasted log):
|
|
94
|
+
→ SKIP: node_modules/*, React internals, engine frames
|
|
95
|
+
→ FOCUS: lines with src/ paths, "Error:", "Caused by:", YOUR component names
|
|
96
|
+
|
|
97
|
+
STEP 3: Search project source code (MANDATORY):
|
|
98
|
+
→ Grep "[keyword]" src/ ← ALWAYS start here (unless BUILD error)
|
|
99
|
+
→ Grep "[function_name]" src/ ← find the actual function
|
|
100
|
+
→ Glob "**/*[ComponentName]*" ← find the actual file
|
|
101
|
+
→ Results: [list files found]
|
|
102
|
+
|
|
103
|
+
STEP 4: Read matched files (TOP 3-5):
|
|
104
|
+
→ Read [file1] — [what I found: actual code, types, imports]
|
|
105
|
+
→ Read [file2] — [what I found: related logic, callers]
|
|
106
|
+
→ Read [file3] — [what I found: state/store connected to this]
|
|
107
|
+
|
|
108
|
+
⛔ If I skipped Step 1-4 → GO BACK AND DO THEM NOW
|
|
109
|
+
⛔ If I found 0 results in src/ → widen: lib/ → app/ → project root
|
|
110
|
+
</project_search>
|
|
68
111
|
|
|
69
112
|
<source_verification>
|
|
70
|
-
⚠️
|
|
71
|
-
- [ ]
|
|
72
|
-
- [ ]
|
|
113
|
+
⚠️ Verify I have REAL project data (not assumptions):
|
|
114
|
+
- [ ] Classified error type and picked correct search strategy
|
|
115
|
+
- [ ] Filtered noise from log (if applicable)
|
|
116
|
+
- [ ] Searched src/ with Grep for error keywords → found files
|
|
117
|
+
- [ ] Read the actual file(s) where bug occurs
|
|
118
|
+
- [ ] Verified function/class names exist in project (grep result)
|
|
73
119
|
- [ ] Checked package versions in package.json/pubspec.yaml
|
|
74
120
|
- [ ] Identified data types from actual code (not assumed)
|
|
121
|
+
- [ ] Traced the call chain: who calls this → what it returns
|
|
75
122
|
</source_verification>
|
|
76
123
|
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
|
|
124
|
+
<root_cause_tracing>
|
|
125
|
+
⛔ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
|
|
126
|
+
|
|
127
|
+
STEP 1 — IMMEDIATE CAUSE (what throws):
|
|
128
|
+
- Error type: [from classification above]
|
|
129
|
+
- Crash/error at: [file:line from project search]
|
|
130
|
+
- What code does at that line: [describe from reading]
|
|
131
|
+
- What value is wrong: [actual vs expected]
|
|
132
|
+
|
|
133
|
+
STEP 2 — TRACE BACKWARD (what called this):
|
|
134
|
+
- Who calls this function? → [grep callers in src/]
|
|
135
|
+
- What data does caller pass? → [trace data origin]
|
|
136
|
+
- Go up one level: who calls the caller? → [trace further]
|
|
137
|
+
|
|
138
|
+
STEP 3 — ROOT CAUSE (where chain breaks):
|
|
139
|
+
- Root cause at: [file:line — where correct data becomes incorrect]
|
|
140
|
+
- WHY it fails: [based on actual code read, NOT guess]
|
|
141
|
+
- Does this match a known pattern? → [check debugging-intelligence.md if loaded]
|
|
142
|
+
- Does root cause explain ALL symptoms? YES/NO
|
|
143
|
+
→ If NO → theory is wrong → re-trace from Step 2
|
|
144
|
+
</root_cause_tracing>
|
|
145
|
+
|
|
146
|
+
<working_example>
|
|
147
|
+
Search for similar working code in SAME project:
|
|
148
|
+
→ Grep for similar pattern that works: [search term]
|
|
149
|
+
→ Found working example at: [file:line] (or "none found")
|
|
150
|
+
→ Differences between broken vs working:
|
|
151
|
+
1. [difference]
|
|
152
|
+
2. [difference]
|
|
153
|
+
→ The fix should align broken code with working pattern
|
|
154
|
+
</working_example>
|
|
155
|
+
|
|
156
|
+
<hypothesis>
|
|
157
|
+
⛔ 1 HYPOTHESIS → 1 MINIMAL CHANGE → VERIFY
|
|
158
|
+
|
|
159
|
+
HYPOTHESIS: [specific theory based on root cause]
|
|
160
|
+
CHANGE: [smallest possible change to test this — 1 change only]
|
|
161
|
+
EXPECTED RESULT: [what should happen if hypothesis is correct]
|
|
162
|
+
|
|
163
|
+
⛔ If this fails → REVERT → form NEW hypothesis (never stack fixes)
|
|
164
|
+
⛔ If 3 hypotheses fail → STOP → question architecture
|
|
165
|
+
</hypothesis>
|
|
90
166
|
|
|
91
167
|
<fix>
|
|
92
|
-
[Specific change with code snippet]
|
|
168
|
+
[Specific change with code snippet — before → after]
|
|
93
169
|
|
|
94
170
|
WHY IT WORKS:
|
|
95
|
-
[Explain
|
|
171
|
+
[Explain based on root cause tracing — not guess]
|
|
96
172
|
SOURCE: [where this fix pattern comes from — project code / skill file / official docs]
|
|
173
|
+
|
|
174
|
+
DEFENSE IN DEPTH (make bug structurally impossible):
|
|
175
|
+
- Layer 1 (input): [validation added]
|
|
176
|
+
- Layer 2 (state): [guard added]
|
|
177
|
+
- Layer 3 (render): [null check / fallback added]
|
|
97
178
|
</fix>
|
|
98
179
|
|
|
180
|
+
<verification>
|
|
181
|
+
⛔ NO COMPLETION CLAIMS WITHOUT EVIDENCE
|
|
182
|
+
|
|
183
|
+
🚩 Anti-rationalization check:
|
|
184
|
+
- Am I saying "should work now" without running it? → RUN IT
|
|
185
|
+
- Am I "confident" without evidence? → PROVE IT
|
|
186
|
+
- Am I stacking 3+ changes? → REVERT. 1 change only.
|
|
187
|
+
|
|
188
|
+
Evidence:
|
|
189
|
+
- [ ] Fix addresses root cause (not just symptoms)
|
|
190
|
+
- [ ] All symptoms explained by this root cause
|
|
191
|
+
- [ ] Working example pattern followed (if found)
|
|
192
|
+
- [ ] Defense-in-depth added at [N] layers
|
|
193
|
+
- [ ] Side effects checked (grep for other callers)
|
|
194
|
+
- [ ] Both platforms considered (iOS + Android)
|
|
195
|
+
</verification>
|
|
196
|
+
|
|
99
197
|
<side_effects>
|
|
100
|
-
- Files that import this: [list
|
|
198
|
+
- Files that import this: [list from grep results]
|
|
101
199
|
- Tests affected: [list]
|
|
102
200
|
- Platform-specific: iOS [impact] / Android [impact]
|
|
103
201
|
</side_effects>
|
|
@@ -116,6 +214,64 @@ FILE: [path]
|
|
|
116
214
|
</think>
|
|
117
215
|
```
|
|
118
216
|
|
|
217
|
+
### Diagnostic Scan (user unsure / vague / "check this for me")
|
|
218
|
+
|
|
219
|
+
```xml
|
|
220
|
+
<think>
|
|
221
|
+
USER SAID: [what user described or asked — could be vague]
|
|
222
|
+
AREA: [extract: screen name / feature name / module name / file name]
|
|
223
|
+
|
|
224
|
+
<area_identification>
|
|
225
|
+
What did user mention or show?
|
|
226
|
+
→ Screen/feature name: [extract from user's words]
|
|
227
|
+
→ File pasted/referenced: [if any]
|
|
228
|
+
→ Behavior described: [if any]
|
|
229
|
+
→ If unclear → I should ask: "Which screen or feature should I check?"
|
|
230
|
+
</area_identification>
|
|
231
|
+
|
|
232
|
+
<project_search>
|
|
233
|
+
Search broadly for this area:
|
|
234
|
+
→ Grep "[feature]" src/ → found: [list files]
|
|
235
|
+
→ Glob "**/*[ScreenName]*" → found: [list files]
|
|
236
|
+
→ Also search: related hooks, services, stores, utils
|
|
237
|
+
→ Total files to scan: [N files]
|
|
238
|
+
</project_search>
|
|
239
|
+
|
|
240
|
+
<diagnostic_scan>
|
|
241
|
+
For EACH file, run the checklist:
|
|
242
|
+
|
|
243
|
+
FILE: [file1:path]
|
|
244
|
+
□ Crash risks: [findings or "clean"]
|
|
245
|
+
□ Memory leaks: [findings or "clean"]
|
|
246
|
+
□ Race conditions: [findings or "clean"]
|
|
247
|
+
□ Security: [findings or "clean"]
|
|
248
|
+
□ Performance: [findings or "clean"]
|
|
249
|
+
□ UX/states: [findings or "clean"]
|
|
250
|
+
□ Data flow: [trace API → state → render — any break?]
|
|
251
|
+
□ Edge cases: [empty data? error response? offline? slow?]
|
|
252
|
+
|
|
253
|
+
FILE: [file2:path]
|
|
254
|
+
□ ... (repeat for each file)
|
|
255
|
+
</diagnostic_scan>
|
|
256
|
+
|
|
257
|
+
<report>
|
|
258
|
+
Scanned: [N files] in [area name]
|
|
259
|
+
|
|
260
|
+
🔴 Issues found:
|
|
261
|
+
1. [SEVERITY] [file:line] — [description]
|
|
262
|
+
2. [SEVERITY] [file:line] — [description]
|
|
263
|
+
|
|
264
|
+
🟡 Suspicious (might be intentional):
|
|
265
|
+
1. [file:line] — [what looks off and why]
|
|
266
|
+
|
|
267
|
+
✅ Looks good:
|
|
268
|
+
- [aspect that's well-implemented]
|
|
269
|
+
|
|
270
|
+
Recommendation: [what to fix first / what to investigate deeper]
|
|
271
|
+
</report>
|
|
272
|
+
</think>
|
|
273
|
+
```
|
|
274
|
+
|
|
119
275
|
**Example:**
|
|
120
276
|
```xml
|
|
121
277
|
<think>
|