feat(build): add /build skill — universal TDD-first feature implementation command
Adds a Cursor slash command that implements any feature end-to-end: deep research → TDD (Red/Green/Refactor) → integration test generation with device pause gate → external setup checklist → verified PR. Includes a FEATURE_REGISTRY covering 7 feature types (notifications, auth, payments, deep links, analytics, storage, camera/media) with per-type test scenarios, external setup steps, and files-to-touch. Stack-aware: adapts to architecture, state management, backend, platforms, and e2e tool from project-brief.yaml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,242 @@
|
|||||||
|
# Design Spec: /build Skill — Universal Feature Implementation Command
|
||||||
|
|
||||||
|
**Date:** 2026-05-14
|
||||||
|
**Status:** Draft — Awaiting user review
|
||||||
|
**Scope:** A single Cursor slash command that orchestrates the full feature development lifecycle
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Problem Statement
|
||||||
|
|
||||||
|
Today, implementing any feature (notifications, auth, payments, etc.) requires the user to manually:
|
||||||
|
- Specify TDD in the prompt
|
||||||
|
- Remember to include integration test scenarios
|
||||||
|
- Know which external services need configuring
|
||||||
|
- Know which files to touch for their specific stack
|
||||||
|
- Invoke the right skills in the right order
|
||||||
|
|
||||||
|
This leads to inconsistent implementations, missed test scenarios, and forgotten native setup steps.
|
||||||
|
|
||||||
|
**Goal:** One command — `/build implement [anything]` — that handles everything from research through PR, following every rule, skill, hook, and architecture pattern defined in the project.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Decision
|
||||||
|
|
||||||
|
**Form:** Cursor slash command skill file (`.md.tmpl` inside the generator's template system)
|
||||||
|
**Scope:** Universal + context-aware (reads `project-brief.yaml` to adapt behavior)
|
||||||
|
**Integration test gate:** Auto-generate `integration_test/` files + pause for device run
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Architecture
|
||||||
|
|
||||||
|
### 3.1 File Placement
|
||||||
|
|
||||||
|
```
|
||||||
|
flutter-cursor-templates/
|
||||||
|
generator/
|
||||||
|
templates/
|
||||||
|
skills/
|
||||||
|
build/
|
||||||
|
SKILL.md.tmpl ← The skill (single new file)
|
||||||
|
test/
|
||||||
|
golden/
|
||||||
|
bloc-clean-firebase/skills/build/SKILL.md
|
||||||
|
riverpod-ff-supabase/skills/build/SKILL.md
|
||||||
|
getx-mvc-rest/skills/build/SKILL.md
|
||||||
|
```
|
||||||
|
|
||||||
|
The generator renders this to `.cursor/skills/build/SKILL.md` in the user's project — same pattern as every other skill.
|
||||||
|
|
||||||
|
### 3.2 Template Variables
|
||||||
|
|
||||||
|
| Placeholder | Source in project-brief.yaml |
|
||||||
|
|---|---|
|
||||||
|
| `{{PROJECT_NAME}}` | `project.name` |
|
||||||
|
| `{{ARCHITECTURE}}` | `stack.architecture` |
|
||||||
|
| `{{STATE_MANAGEMENT}}` | `stack.state_management` |
|
||||||
|
| `{{ROUTING}}` | `stack.routing` |
|
||||||
|
| `{{BACKEND}}` | `stack.backend` |
|
||||||
|
| `{{PLATFORMS_LIST}}` | `stack.platforms` |
|
||||||
|
| `{{CODEGEN_LIST}}` | `stack.codegen` |
|
||||||
|
| `{{TESTING_DEPTH}}` | `testing.depth` |
|
||||||
|
| `{{E2E_TOOL}}` | `testing.e2e_tool` |
|
||||||
|
| `{{FEATURES_LIST}}` | `features.modules` |
|
||||||
|
| `{{SPECIAL_FEATURES}}` | `features.special` |
|
||||||
|
| `{{FLAVORS_LIST}}` | `environments.flavors` |
|
||||||
|
| `{{CICD_TOOL}}` | `environments.cicd` |
|
||||||
|
| `{{PACKAGE_ID}}` | `project.package` |
|
||||||
|
|
||||||
|
Conditional blocks: `{{#if backend_firebase}}`, `{{#if platforms_ios}}`, `{{#if platforms_android}}`, `{{#if codegen_freezed}}`, `{{#if codegen_injectable}}`, `{{#if special_push_notifications}}`, `{{#if special_deep_linking}}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. The 7 Phases
|
||||||
|
|
||||||
|
### Phase 1: Context Loading
|
||||||
|
1. Read `project-brief.yaml` — extract stack, platforms, existing features
|
||||||
|
2. Parse user's request → match against FEATURE_REGISTRY keywords
|
||||||
|
3. Print context summary table (feature type, stack, platforms, rules loaded)
|
||||||
|
4. If feature already in `features.modules`, confirm extending vs. rebuilding
|
||||||
|
|
||||||
|
### Phase 2: Deep Research
|
||||||
|
1. Enumerate required packages + correct versions for current Flutter stable
|
||||||
|
2. Build complete file manifest: new files (by architecture pattern) + modified files
|
||||||
|
3. List all external service configuration required per platform
|
||||||
|
4. Print research results before touching any file
|
||||||
|
|
||||||
|
### Phase 3: TDD Implementation
|
||||||
|
**Invokes: `superpowers:test-driven-development`**
|
||||||
|
|
||||||
|
- **Red:** Write all failing unit + widget tests from FEATURE_REGISTRY scenarios. Run to confirm red. Show actual output.
|
||||||
|
- **Green:** Implement domain → data → presentation layers. Run tests to green. Show actual output.
|
||||||
|
- **Refactor:** Clean up per `flutter-core.mdc`. Run `flutter analyze`. Show output.
|
||||||
|
|
||||||
|
### Phase 4: Integration Test Generation
|
||||||
|
1. Create `integration_test/[feature_type]/` directory
|
||||||
|
2. Generate one test file per logical scenario cluster
|
||||||
|
3. Each file: standard boilerplate with `IntegrationTestWidgetsFlutterBinding` (or Patrol if `{{E2E_TOOL}} = patrol`)
|
||||||
|
4. Generate `integration_test/[feature]/README.md` — test matrix with run commands
|
||||||
|
|
||||||
|
**PAUSE GATE:** Print table of all integration test files, what they cover, whether hardware is required, and the exact `flutter test` commands. Wait for user to paste device output before Phase 6.
|
||||||
|
|
||||||
|
### Phase 5: External Setup Checklist
|
||||||
|
- Grouped by service/platform (Firebase, iOS, Android, backend)
|
||||||
|
- iOS/Android: table format — Step | Where | What | How to Verify
|
||||||
|
- Console steps: numbered checklist with exact menu paths and URLs
|
||||||
|
|
||||||
|
### Phase 6: Verification Gate
|
||||||
|
**Invokes: `superpowers:verification-before-completion`**
|
||||||
|
|
||||||
|
Required evidence (real output pasted by user):
|
||||||
|
- `flutter test test/features/[feature]/ --no-pub` → all tests pass
|
||||||
|
- `flutter analyze` → no issues
|
||||||
|
- Integration test device output from Phase 4
|
||||||
|
- `lefthook run pre-commit` → all hooks pass
|
||||||
|
|
||||||
|
If any failure: invoke `superpowers:systematic-debugging` before retrying.
|
||||||
|
|
||||||
|
### Phase 7: PR Preparation
|
||||||
|
**Invokes: `superpowers:finishing-a-development-branch`**
|
||||||
|
|
||||||
|
- Conventional commit: `feat([feature_type]): implement [feature] end-to-end`
|
||||||
|
- PR description: what built, test count, integration test matrix, external setup status
|
||||||
|
- CI/CD advice for `{{CICD_TOOL}}` (secret scoping per flavor)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. FEATURE_REGISTRY
|
||||||
|
|
||||||
|
Seven feature types with full metadata:
|
||||||
|
|
||||||
|
| Feature Type | Keywords (sample) | Packages | Integration Test Scenarios |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `notifications` | notification, push, FCM, APNs | firebase_messaging, flutter_local_notifications | foreground/background/killed receipt, tap redirect in all 3 states, payload parsing |
|
||||||
|
| `auth` | login, sign in, biometric, OAuth, JWT | firebase_auth, local_auth, flutter_secure_storage | full login flow, biometric + fallback, token refresh, cold start with session |
|
||||||
|
| `payments` | payment, Stripe, IAP, checkout | flutter_stripe, purchases_flutter | test-card checkout, 3DS, Apple/Google Pay, subscription restore |
|
||||||
|
| `deep_links` | deep link, universal link, app link | app_links, go_router | cold/background/foreground link handling, auth-guarded routes |
|
||||||
|
| `analytics` | analytics, event, tracking, screen view | firebase_analytics, mixpanel_flutter | event triggered on action, screen view on nav, opt-out disables tracking |
|
||||||
|
| `storage` | file upload, Firebase Storage, Hive, Isar | firebase_storage, isar, drift | upload + retrieve, offline cache, background upload restore |
|
||||||
|
| `camera_media` | camera, photo, QR, barcode, video | camera, image_picker, mobile_scanner | photo capture, gallery pick, QR decode, permission denied UI |
|
||||||
|
|
||||||
|
Each entry also specifies: `rules_to_load`, `files_to_touch` (new + modified), `external_setup` (per service/platform), `unit_test_scenarios`, `widget_test_scenarios`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Integration Test Template Structure
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// integration_test/[feature]/[scenario]_test.dart
|
||||||
|
// Generated by /build — {{PROJECT_NAME}}
|
||||||
|
// Run: flutter test integration_test/[feature]/[scenario]_test.dart -d <device_id>
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:{{PACKAGE_ID}}/main.dart' as app;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
group('[FeatureType] — [Scenario Name]', () {
|
||||||
|
setUp(() async { /* seed state / reset storage */ });
|
||||||
|
tearDown(() async { /* cleanup */ });
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'given [precondition], when [action], then [expected outcome]',
|
||||||
|
(tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
// test body
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For `{{E2E_TOOL}} = patrol`: imports swap to `package:patrol`, uses `$` Patrol selector syntax.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Skill File Sections (exact headings)
|
||||||
|
|
||||||
|
```
|
||||||
|
# Build — {{PROJECT_NAME}}
|
||||||
|
## Usage
|
||||||
|
## FEATURE_REGISTRY
|
||||||
|
## Phase 1: Context Loading
|
||||||
|
## Phase 2: Deep Research
|
||||||
|
## Phase 3: TDD Implementation
|
||||||
|
## Phase 4: Integration Test Generation
|
||||||
|
## Phase 5: External Setup Checklist
|
||||||
|
## Phase 6: Verification Gate
|
||||||
|
## Phase 7: PR Preparation
|
||||||
|
## Rules applied every phase
|
||||||
|
## Code generation notes
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Cross-Skill Orchestration
|
||||||
|
|
||||||
|
The `/build` skill orchestrates existing skills internally:
|
||||||
|
- Invokes `/scaffold-feature` patterns to create the initial file skeleton (Phase 3b)
|
||||||
|
- Borrows `/generate-tests` naming and structure conventions (Phase 3a)
|
||||||
|
- Invokes `/generate-api-client` if `api_docs.format != none` and feature needs API (Phase 2)
|
||||||
|
- References `/deploy` checklist for flavor-scoped secret handling (Phase 7)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Rules Applied Every Phase
|
||||||
|
|
||||||
|
Always active (no conditional):
|
||||||
|
- `.cursor/rules/flutter-core.mdc`
|
||||||
|
- `.cursor/rules/security-standards.mdc`
|
||||||
|
- `.cursor/rules/project-context.mdc`
|
||||||
|
- Feature-type rules from `FEATURE_REGISTRY.rules_to_load`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Enhancements / Out of Scope for V1
|
||||||
|
|
||||||
|
- Reference project URL fetching and pattern extraction (Phase 1) — included in V1
|
||||||
|
- Multi-feature requests spanning 2+ feature types — detect and prompt decomposition
|
||||||
|
- Automatic CI secret rotation advice — Phase 7, advisory only
|
||||||
|
- Web platform integration test support — flagged as TODO in generated README
|
||||||
|
- Visual companion for architecture diagrams — post-V1
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Verification
|
||||||
|
|
||||||
|
End-to-end test of the skill:
|
||||||
|
|
||||||
|
1. Run `cursor_gen --wizard` on a test project → choose `bloc/clean/firebase` stack
|
||||||
|
2. Confirm `.cursor/skills/build/SKILL.md` is generated
|
||||||
|
3. In Cursor, type `/build implement notification module end-to-end`
|
||||||
|
4. Verify Phase 1 prints correct context table from project-brief.yaml
|
||||||
|
5. Verify Phase 3 tests are red before implementation, green after
|
||||||
|
6. Verify Phase 4 generates `integration_test/notifications/` with 10 test files
|
||||||
|
7. Verify Phase 5 checklist lists Firebase Console + APNs steps
|
||||||
|
8. Verify Phase 6 requires real output before proceeding
|
||||||
|
9. Run golden tests: `dart test test/generator_test.dart` — all stacks must match golden SKILL.md
|
||||||
@@ -72,6 +72,7 @@ class Resolver {
|
|||||||
'skills/scaffold-feature',
|
'skills/scaffold-feature',
|
||||||
'skills/scaffold-screen',
|
'skills/scaffold-screen',
|
||||||
'skills/generate-tests',
|
'skills/generate-tests',
|
||||||
|
'skills/build',
|
||||||
]);
|
]);
|
||||||
if (brief.apiDocsFormat != 'none') files.add('skills/generate-api-client');
|
if (brief.apiDocsFormat != 'none') files.add('skills/generate-api-client');
|
||||||
if (brief.flavors.length > 1) files.add('skills/create-flavor');
|
if (brief.flavors.length > 1) files.add('skills/create-flavor');
|
||||||
@@ -123,6 +124,7 @@ class Resolver {
|
|||||||
if (key.contains('i18n')) return 'localization.enabled: true';
|
if (key.contains('i18n')) return 'localization.enabled: true';
|
||||||
if (key.contains('migration')) return 'state_management is GetX — migration guidance included';
|
if (key.contains('migration')) return 'state_management is GetX — migration guidance included';
|
||||||
if (key.contains('security-agent')) return 'scale: ${brief.scale} or auth is configured';
|
if (key.contains('security-agent')) return 'scale: ${brief.scale} or auth is configured';
|
||||||
|
if (key == 'skills/build') return 'Always included — universal TDD-first feature implementation command';
|
||||||
if (key.contains('api-client')) return 'api_docs.format is set';
|
if (key.contains('api-client')) return 'api_docs.format is set';
|
||||||
if (key.contains('realtime')) return 'features.special contains realtime';
|
if (key.contains('realtime')) return 'features.special contains realtime';
|
||||||
return 'Included';
|
return 'Included';
|
||||||
|
|||||||
@@ -0,0 +1,568 @@
|
|||||||
|
# Build — {{PROJECT_NAME}}
|
||||||
|
|
||||||
|
Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR.
|
||||||
|
Stack: **{{STATE_MANAGEMENT}}** / **{{ARCHITECTURE}}** / **{{BACKEND}}** / {{PLATFORMS_LIST}}.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/build <free-text description of what to implement>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- `/build implement notification module end-to-end`
|
||||||
|
- `/build add biometric auth`
|
||||||
|
- `/build integrate Stripe payments`
|
||||||
|
- `/build add deep linking for shared product pages`
|
||||||
|
- `/build implement analytics event tracking`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FEATURE_REGISTRY
|
||||||
|
|
||||||
|
The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
feature_registry:
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
keywords: [notification, push, fcm, apns, alert, badge, silent push,
|
||||||
|
remote notification, local notification, firebase messaging]
|
||||||
|
pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- parse notification payload to domain model
|
||||||
|
- FCM token generation and storage
|
||||||
|
- token refresh triggers re-registration
|
||||||
|
- notification permission denied returns graceful fallback
|
||||||
|
- notification service initialisation is idempotent
|
||||||
|
widget_test_scenarios:
|
||||||
|
- notification banner renders correct title and body
|
||||||
|
- tap on notification navigates to correct route
|
||||||
|
- badge count updates on new message
|
||||||
|
integration_test_scenarios:
|
||||||
|
- foreground notification receipt and display
|
||||||
|
- background notification receipt (app backgrounded, not killed)
|
||||||
|
- killed-state notification receipt (cold launch from notification tap)
|
||||||
|
- tap-to-open in foreground state routes correctly
|
||||||
|
- tap-to-open in background state routes correctly
|
||||||
|
- tap-to-open in killed state routes correctly
|
||||||
|
- notification payload parsing end-to-end
|
||||||
|
- deep link routing from notification data field
|
||||||
|
- multiple simultaneous notifications (ordering and dedup)
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist]
|
||||||
|
ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase]
|
||||||
|
android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/notifications/, integration_test/notifications/]
|
||||||
|
modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml]
|
||||||
|
|
||||||
|
auth:
|
||||||
|
keywords: [auth, authentication, login, sign in, sign out, logout,
|
||||||
|
biometric, face id, touch id, fingerprint, oauth, jwt,
|
||||||
|
session, token refresh, social login, google sign-in]
|
||||||
|
pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- login success stores token securely
|
||||||
|
- login failure (wrong password) returns typed error
|
||||||
|
- login failure (network) returns typed error
|
||||||
|
- token refresh succeeds and updates stored token
|
||||||
|
- token refresh failure triggers logout
|
||||||
|
- biometric auth success grants access
|
||||||
|
- biometric auth failure falls back to password
|
||||||
|
- biometric not enrolled returns correct error state
|
||||||
|
- logout clears all stored credentials
|
||||||
|
- session persistence: token loaded on cold start
|
||||||
|
widget_test_scenarios:
|
||||||
|
- login form renders correctly
|
||||||
|
- validation errors shown inline
|
||||||
|
- loading state disables submit button
|
||||||
|
- biometric prompt shown when available
|
||||||
|
integration_test_scenarios:
|
||||||
|
- complete login flow (email and password)
|
||||||
|
- login then logout then login again
|
||||||
|
- invalid credentials shows error and stays on login
|
||||||
|
- biometric login flow on enrolled device
|
||||||
|
- biometric fallback to password
|
||||||
|
- token refresh in background while user navigates
|
||||||
|
- cold start with stored valid session skips login
|
||||||
|
- cold start with expired session redirects to login
|
||||||
|
- social auth OAuth redirect and return
|
||||||
|
- deep link into protected route redirects to login then back
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Email/Password provider, Enable Google provider, download updated config files]
|
||||||
|
ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode]
|
||||||
|
android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/auth/, integration_test/auth/]
|
||||||
|
modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
payments:
|
||||||
|
keywords: [payment, stripe, in-app purchase, iap, checkout, subscription,
|
||||||
|
billing, apple pay, google pay, card, transaction, refund, revenue cat]
|
||||||
|
pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- checkout request builds correct PaymentIntent params
|
||||||
|
- payment success updates order state
|
||||||
|
- payment failure (card declined) returns typed error
|
||||||
|
- webhook event parsed to domain model
|
||||||
|
- refund request constructs correct API call
|
||||||
|
- subscription status checked on app resume
|
||||||
|
widget_test_scenarios:
|
||||||
|
- checkout form renders with correct amount
|
||||||
|
- payment loading state shown during processing
|
||||||
|
- success confirmation screen renders
|
||||||
|
- error state with retry option
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end checkout with test card (Stripe test mode)
|
||||||
|
- checkout with 3D Secure challenge
|
||||||
|
- card declined shows error then retry succeeds
|
||||||
|
- Apple Pay / Google Pay sheet appears (device capability check)
|
||||||
|
- subscription purchase and entitlement unlock
|
||||||
|
- subscription restore flow
|
||||||
|
- refund flow from order history
|
||||||
|
external_setup:
|
||||||
|
stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration]
|
||||||
|
ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement]
|
||||||
|
android: [Google Pay Console setup, BILLING permission for in_app_purchase]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/payments/, integration_test/payments/]
|
||||||
|
modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml]
|
||||||
|
|
||||||
|
deep_links:
|
||||||
|
keywords: [deep link, deep linking, universal link, app link, deferred deep link,
|
||||||
|
dynamic link, branch, uri scheme, custom scheme, url scheme]
|
||||||
|
pub_packages: [go_router, app_links, uni_links]
|
||||||
|
rules_to_load: [platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- URI parsed to correct route and parameters
|
||||||
|
- unknown URI falls back to home
|
||||||
|
- authenticated-only route redirects to login when unauthenticated
|
||||||
|
- deep link preserves query parameters
|
||||||
|
widget_test_scenarios:
|
||||||
|
- navigation guard redirects unauthenticated deep link
|
||||||
|
integration_test_scenarios:
|
||||||
|
- cold start from Universal Link routes to correct screen
|
||||||
|
- cold start from custom URI scheme routes correctly
|
||||||
|
- backgrounded app receives deep link and navigates
|
||||||
|
- foreground app receives deep link and navigates
|
||||||
|
- deep link to authenticated route redirects to login then original destination
|
||||||
|
- deep link with path parameters loads correct content
|
||||||
|
- invalid or malformed deep link shows 404 screen
|
||||||
|
external_setup:
|
||||||
|
ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/]
|
||||||
|
android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/]
|
||||||
|
modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist]
|
||||||
|
|
||||||
|
analytics:
|
||||||
|
keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics,
|
||||||
|
segment, posthog, screen view, funnel, cohort]
|
||||||
|
pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter]
|
||||||
|
rules_to_load: [security-standards.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- analytics service logs correct event name and params
|
||||||
|
- PII fields stripped before event sent
|
||||||
|
- analytics disabled in dev flavor
|
||||||
|
- screen name logged on navigation
|
||||||
|
widget_test_scenarios:
|
||||||
|
- RouteObserver triggers screen_view event
|
||||||
|
integration_test_scenarios:
|
||||||
|
- user action triggers expected event (verify via debug view)
|
||||||
|
- screen transitions log screen_view with correct names
|
||||||
|
- opt-out disables all tracking
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/]
|
||||||
|
modified: [lib/main.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
storage:
|
||||||
|
keywords: [storage, file upload, download, cloud storage, firebase storage,
|
||||||
|
supabase storage, s3, image upload, document, file picker,
|
||||||
|
offline, cache, hive, isar, objectbox, sqflite, drift]
|
||||||
|
pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- upload returns public URL on success
|
||||||
|
- upload failure returns typed error
|
||||||
|
- local cache read before remote fetch (offline-first)
|
||||||
|
- cache invalidation on TTL expiry
|
||||||
|
- large file upload uses resumable upload
|
||||||
|
widget_test_scenarios:
|
||||||
|
- file picker button triggers picker
|
||||||
|
- upload progress indicator shown
|
||||||
|
- image preview renders after selection
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end file upload and retrieval
|
||||||
|
- offline mode: local cache serves data
|
||||||
|
- background upload completes when connectivity restored
|
||||||
|
- file size limit enforced
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable]
|
||||||
|
ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist]
|
||||||
|
android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/storage/, integration_test/storage/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
|
||||||
|
camera_media:
|
||||||
|
keywords: [camera, photo, video, image picker, qr code, barcode scanner,
|
||||||
|
ar, augmented reality, gallery, media, record, capture]
|
||||||
|
pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- QR/barcode parsed to correct domain model
|
||||||
|
- image compressed before upload
|
||||||
|
- camera permission denied returns typed error
|
||||||
|
widget_test_scenarios:
|
||||||
|
- camera preview widget renders
|
||||||
|
- capture button triggers photo take
|
||||||
|
integration_test_scenarios:
|
||||||
|
- camera opens and captures photo on real device
|
||||||
|
- gallery picker selects image and returns
|
||||||
|
- QR scan decodes valid code correctly
|
||||||
|
- permission denied shows correct error UI
|
||||||
|
external_setup:
|
||||||
|
ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video]
|
||||||
|
android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/camera/, integration_test/camera/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Context Loading
|
||||||
|
|
||||||
|
**When the user types `/build <request>`, do the following before writing a single line of code:**
|
||||||
|
|
||||||
|
1. Read `project-brief.yaml` from the repo root. Extract and hold in context:
|
||||||
|
- `stack.state_management` → **{{STATE_MGMT_RAW}}**
|
||||||
|
- `stack.architecture` → **{{ARCH_RAW}}**
|
||||||
|
- `stack.routing` → **{{ROUTING_RAW}}**
|
||||||
|
- `stack.backend` → **{{BACKENDS_LIST}}**
|
||||||
|
- `stack.platforms` → **{{PLATFORMS_LIST}}**
|
||||||
|
- `stack.codegen` → **{{CODEGEN_LIST}}**
|
||||||
|
- `testing.depth` → **{{TESTING_DEPTH}}**
|
||||||
|
- `testing.e2e_tool` → **{{E2E_TOOL}}**
|
||||||
|
- `features.modules` (existing features) → **{{FEATURES_LIST}}**
|
||||||
|
- `features.special` → **{{SPECIAL_FEATURES}}**
|
||||||
|
- `environments.flavors` → **{{FLAVORS_LIST}}**
|
||||||
|
- `environments.cicd` → **{{CICD_RAW}}**
|
||||||
|
|
||||||
|
2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features.
|
||||||
|
|
||||||
|
3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type.
|
||||||
|
|
||||||
|
4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference.
|
||||||
|
|
||||||
|
5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)"
|
||||||
|
|
||||||
|
6. Print the context summary table before proceeding:
|
||||||
|
|
||||||
|
```
|
||||||
|
| Field | Value |
|
||||||
|
|------------------|------------------------------|
|
||||||
|
| Feature type | [detected type] |
|
||||||
|
| State management | {{STATE_MANAGEMENT}} |
|
||||||
|
| Architecture | {{ARCHITECTURE}} |
|
||||||
|
| Backend | {{BACKEND}} |
|
||||||
|
| Platforms | {{PLATFORMS_LIST}} |
|
||||||
|
| E2E tool | {{E2E_TOOL}} |
|
||||||
|
| Rules loaded | [list from rules_to_load] |
|
||||||
|
| Existing modules | {{FEATURES_LIST}} |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Deep Research
|
||||||
|
|
||||||
|
**Before touching any file, print the full research output.**
|
||||||
|
|
||||||
|
1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions.
|
||||||
|
|
||||||
|
2. Build the complete file manifest, adapting to `{{ARCHITECTURE}}`:
|
||||||
|
|
||||||
|
**For {{ARCHITECTURE}} ({{ARCH_RAW}}):**
|
||||||
|
- `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/`
|
||||||
|
- `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/`
|
||||||
|
- `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/`
|
||||||
|
|
||||||
|
**State management file naming ({{STATE_MANAGEMENT}} / {{STATE_MGMT_RAW}}):**
|
||||||
|
- `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart`
|
||||||
|
- `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart`
|
||||||
|
- `getx`: `[feature]_controller.dart`, `[feature]_binding.dart`
|
||||||
|
|
||||||
|
3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file.
|
||||||
|
|
||||||
|
4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `{{PLATFORMS_LIST}}`.
|
||||||
|
|
||||||
|
5. Print research results in this format:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Research Results
|
||||||
|
|
||||||
|
### Packages to add to pubspec.yaml
|
||||||
|
- [package_name]: ^[version]
|
||||||
|
|
||||||
|
### Files to create ({{ARCHITECTURE}} / {{STATE_MANAGEMENT}})
|
||||||
|
lib/features/[name]/... (full list)
|
||||||
|
|
||||||
|
### Files to modify
|
||||||
|
[file_path] — [what changes]
|
||||||
|
|
||||||
|
### External services requiring configuration
|
||||||
|
- [Service] ([platform]) — [what to do]
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not touch any file until the user has seen this output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: TDD Implementation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:test-driven-development`**
|
||||||
|
|
||||||
|
Follow Red → Green → Refactor strictly. Show actual terminal output at each step.
|
||||||
|
|
||||||
|
### Step 3a — Red: Write all failing tests first
|
||||||
|
|
||||||
|
1. Mirror the feature directory under `test/features/[feature]/`.
|
||||||
|
2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type.
|
||||||
|
3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`.
|
||||||
|
4. Use `mocktail` for all dependencies.
|
||||||
|
5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'`
|
||||||
|
6. Use the state management test pattern for **{{STATE_MANAGEMENT}}**:
|
||||||
|
```
|
||||||
|
{{TEST_PATTERN}}
|
||||||
|
```
|
||||||
|
7. Run and confirm red:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3b — Green: Implement
|
||||||
|
|
||||||
|
1. Create domain entities and repository interfaces.
|
||||||
|
2. Create data-layer implementations wiring to `{{BACKEND}}`.
|
||||||
|
3. Create presentation layer using **{{STATE_MANAGEMENT}}** patterns.
|
||||||
|
4. Register in DI container — follow `{{ARCH_IMPORT_RULES}}`.
|
||||||
|
5. Wire the route in `{{ROUTING}}` router.
|
||||||
|
6. If `{{CODEGEN_LIST}}` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models.
|
||||||
|
7. Run tests and confirm green:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3c — Refactor
|
||||||
|
|
||||||
|
1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`.
|
||||||
|
2. Run and confirm clean:
|
||||||
|
```
|
||||||
|
flutter analyze
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
3. Run tests once more to confirm still green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Integration Test Generation
|
||||||
|
|
||||||
|
**Generate `integration_test/[feature_type]/` before asking the user to run anything.**
|
||||||
|
|
||||||
|
### Integration test file structure
|
||||||
|
|
||||||
|
Each test file follows this template:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// integration_test/[feature]/[scenario]_test.dart
|
||||||
|
// Generated by /build — {{PROJECT_NAME}}
|
||||||
|
// Feature: [feature_type] | Scenario: [scenario_name]
|
||||||
|
// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d <device_id>
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:{{PACKAGE_ID}}/main.dart' as app;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
group('[FeatureType] — [Scenario Name]', () {
|
||||||
|
setUp(() async {
|
||||||
|
// Seed state / configure mocks / reset storage
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
// Cleanup
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'given [precondition], when [action], then [expected outcome]',
|
||||||
|
(tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
// test body
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**For `{{E2E_TOOL}}` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax.
|
||||||
|
|
||||||
|
### Files to generate
|
||||||
|
|
||||||
|
Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`.
|
||||||
|
Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands.
|
||||||
|
|
||||||
|
### PAUSE GATE — user must run on device
|
||||||
|
|
||||||
|
After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6.
|
||||||
|
|
||||||
|
```
|
||||||
|
## ACTION REQUIRED — Run Integration Tests on Real Device
|
||||||
|
|
||||||
|
| File | Covers | Requires Hardware |
|
||||||
|
|------|--------|------------------|
|
||||||
|
| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No |
|
||||||
|
...
|
||||||
|
|
||||||
|
Run on iOS:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_ios_device_id>
|
||||||
|
|
||||||
|
Run on Android:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_android_device_id>
|
||||||
|
|
||||||
|
Paste the output here to continue.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: External Setup Checklist
|
||||||
|
|
||||||
|
**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `{{PLATFORMS_LIST}}`.**
|
||||||
|
|
||||||
|
### Format for console/service steps (numbered list):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Firebase (backend: {{BACKEND}})
|
||||||
|
- [ ] 1. Open Firebase Console → [exact menu path]
|
||||||
|
- [ ] 2. [Specific action]
|
||||||
|
- [ ] 3. Download updated config file → place at [exact path]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format for native platform steps (table):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### iOS Setup
|
||||||
|
| Step | Where | What | How to Verify |
|
||||||
|
|------|-------|------|---------------|
|
||||||
|
| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] |
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Android Setup
|
||||||
|
- [ ] 1. Open `android/app/src/main/AndroidManifest.xml`
|
||||||
|
- Add: `<uses-permission android:name="[permission]"/>`
|
||||||
|
- [ ] 2. [Next step with exact value]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flavor scoping note (flavors: {{FLAVORS_LIST}})
|
||||||
|
|
||||||
|
Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Verification Gate
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:verification-before-completion`**
|
||||||
|
|
||||||
|
**Do not claim completion without pasting real output for every item below.**
|
||||||
|
|
||||||
|
```
|
||||||
|
VERIFICATION REQUIRED — paste real output for each:
|
||||||
|
|
||||||
|
[ ] flutter test test/features/[feature]/ --no-pub
|
||||||
|
Required: "All N tests passed."
|
||||||
|
|
||||||
|
[ ] flutter analyze
|
||||||
|
Required: "No issues found!"
|
||||||
|
|
||||||
|
[ ] Integration test device output (from Phase 4 pause gate)
|
||||||
|
Required: actual device test output
|
||||||
|
|
||||||
|
[ ] lefthook run pre-commit
|
||||||
|
Required: all hooks passed
|
||||||
|
```
|
||||||
|
|
||||||
|
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
|
||||||
|
Do not proceed to Phase 7 until all four checks are green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: PR Preparation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:finishing-a-development-branch`**
|
||||||
|
|
||||||
|
1. **Commit message** (conventional commits format):
|
||||||
|
```
|
||||||
|
feat([feature_type]): implement [feature] end-to-end
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **PR description template:**
|
||||||
|
```markdown
|
||||||
|
## What
|
||||||
|
[One sentence: what feature was implemented]
|
||||||
|
|
||||||
|
## Test coverage
|
||||||
|
- Unit/widget tests: N passing
|
||||||
|
- Integration test matrix:
|
||||||
|
| Scenario | iOS | Android |
|
||||||
|
|----------|-----|---------|
|
||||||
|
| [scenario] | ✅ | ✅ |
|
||||||
|
|
||||||
|
## External setup completed
|
||||||
|
- [ ] [Service 1 setup]
|
||||||
|
- [ ] [Service 2 setup]
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
[Any follow-up TODOs or known limitations]
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **CI/CD advice for {{CICD_TOOL}} ({{CICD_RAW}}):**
|
||||||
|
Ensure workflow secrets are scoped per flavor ({{FLAVORS_LIST}}). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rules applied every phase
|
||||||
|
|
||||||
|
Always active — read before writing any code:
|
||||||
|
- `.cursor/rules/flutter-core.mdc`
|
||||||
|
- `.cursor/rules/security-standards.mdc`
|
||||||
|
- `.cursor/rules/project-context.mdc`
|
||||||
|
- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load`
|
||||||
|
|
||||||
|
Architecture import rules for **{{ARCHITECTURE}}**:
|
||||||
|
{{ARCH_IMPORT_RULES}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code generation notes
|
||||||
|
|
||||||
|
**Codegen tools configured: {{CODEGEN_LIST}}**
|
||||||
|
|
||||||
|
After adding any new model or injectable class, run:
|
||||||
|
```
|
||||||
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them.
|
||||||
|
|
||||||
|
**Template version:** {{TEMPLATE_VERSION}}
|
||||||
@@ -243,6 +243,12 @@ void main() {
|
|||||||
expect(files.length, equals(unique.length),
|
expect(files.length, equals(unique.length),
|
||||||
reason: 'Duplicate template files detected');
|
reason: 'Duplicate template files detected');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('build skill always included in resolved list', () {
|
||||||
|
expect(Resolver.resolve(_blocCleanBrief), contains('skills/build'));
|
||||||
|
expect(Resolver.resolve(_riverpodFFBrief), contains('skills/build'));
|
||||||
|
expect(Resolver.resolve(_getxMvcBrief), contains('skills/build'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Renderer / placeholder tests ───────────────────────────────────────────
|
// ─── Renderer / placeholder tests ───────────────────────────────────────────
|
||||||
|
|||||||
+574
@@ -0,0 +1,574 @@
|
|||||||
|
# Build — TestApp
|
||||||
|
|
||||||
|
Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR.
|
||||||
|
Stack: **BLoC / Cubit** / **Clean Architecture** / **Firebase** / ios, android.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/build <free-text description of what to implement>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- `/build implement notification module end-to-end`
|
||||||
|
- `/build add biometric auth`
|
||||||
|
- `/build integrate Stripe payments`
|
||||||
|
- `/build add deep linking for shared product pages`
|
||||||
|
- `/build implement analytics event tracking`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FEATURE_REGISTRY
|
||||||
|
|
||||||
|
The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
feature_registry:
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
keywords: [notification, push, fcm, apns, alert, badge, silent push,
|
||||||
|
remote notification, local notification, firebase messaging]
|
||||||
|
pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- parse notification payload to domain model
|
||||||
|
- FCM token generation and storage
|
||||||
|
- token refresh triggers re-registration
|
||||||
|
- notification permission denied returns graceful fallback
|
||||||
|
- notification service initialisation is idempotent
|
||||||
|
widget_test_scenarios:
|
||||||
|
- notification banner renders correct title and body
|
||||||
|
- tap on notification navigates to correct route
|
||||||
|
- badge count updates on new message
|
||||||
|
integration_test_scenarios:
|
||||||
|
- foreground notification receipt and display
|
||||||
|
- background notification receipt (app backgrounded, not killed)
|
||||||
|
- killed-state notification receipt (cold launch from notification tap)
|
||||||
|
- tap-to-open in foreground state routes correctly
|
||||||
|
- tap-to-open in background state routes correctly
|
||||||
|
- tap-to-open in killed state routes correctly
|
||||||
|
- notification payload parsing end-to-end
|
||||||
|
- deep link routing from notification data field
|
||||||
|
- multiple simultaneous notifications (ordering and dedup)
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist]
|
||||||
|
ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase]
|
||||||
|
android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/notifications/, integration_test/notifications/]
|
||||||
|
modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml]
|
||||||
|
|
||||||
|
auth:
|
||||||
|
keywords: [auth, authentication, login, sign in, sign out, logout,
|
||||||
|
biometric, face id, touch id, fingerprint, oauth, jwt,
|
||||||
|
session, token refresh, social login, google sign-in]
|
||||||
|
pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- login success stores token securely
|
||||||
|
- login failure (wrong password) returns typed error
|
||||||
|
- login failure (network) returns typed error
|
||||||
|
- token refresh succeeds and updates stored token
|
||||||
|
- token refresh failure triggers logout
|
||||||
|
- biometric auth success grants access
|
||||||
|
- biometric auth failure falls back to password
|
||||||
|
- biometric not enrolled returns correct error state
|
||||||
|
- logout clears all stored credentials
|
||||||
|
- session persistence: token loaded on cold start
|
||||||
|
widget_test_scenarios:
|
||||||
|
- login form renders correctly
|
||||||
|
- validation errors shown inline
|
||||||
|
- loading state disables submit button
|
||||||
|
- biometric prompt shown when available
|
||||||
|
integration_test_scenarios:
|
||||||
|
- complete login flow (email and password)
|
||||||
|
- login then logout then login again
|
||||||
|
- invalid credentials shows error and stays on login
|
||||||
|
- biometric login flow on enrolled device
|
||||||
|
- biometric fallback to password
|
||||||
|
- token refresh in background while user navigates
|
||||||
|
- cold start with stored valid session skips login
|
||||||
|
- cold start with expired session redirects to login
|
||||||
|
- social auth OAuth redirect and return
|
||||||
|
- deep link into protected route redirects to login then back
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Email/Password provider, Enable Google provider, download updated config files]
|
||||||
|
ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode]
|
||||||
|
android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/auth/, integration_test/auth/]
|
||||||
|
modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
payments:
|
||||||
|
keywords: [payment, stripe, in-app purchase, iap, checkout, subscription,
|
||||||
|
billing, apple pay, google pay, card, transaction, refund, revenue cat]
|
||||||
|
pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- checkout request builds correct PaymentIntent params
|
||||||
|
- payment success updates order state
|
||||||
|
- payment failure (card declined) returns typed error
|
||||||
|
- webhook event parsed to domain model
|
||||||
|
- refund request constructs correct API call
|
||||||
|
- subscription status checked on app resume
|
||||||
|
widget_test_scenarios:
|
||||||
|
- checkout form renders with correct amount
|
||||||
|
- payment loading state shown during processing
|
||||||
|
- success confirmation screen renders
|
||||||
|
- error state with retry option
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end checkout with test card (Stripe test mode)
|
||||||
|
- checkout with 3D Secure challenge
|
||||||
|
- card declined shows error then retry succeeds
|
||||||
|
- Apple Pay / Google Pay sheet appears (device capability check)
|
||||||
|
- subscription purchase and entitlement unlock
|
||||||
|
- subscription restore flow
|
||||||
|
- refund flow from order history
|
||||||
|
external_setup:
|
||||||
|
stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration]
|
||||||
|
ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement]
|
||||||
|
android: [Google Pay Console setup, BILLING permission for in_app_purchase]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/payments/, integration_test/payments/]
|
||||||
|
modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml]
|
||||||
|
|
||||||
|
deep_links:
|
||||||
|
keywords: [deep link, deep linking, universal link, app link, deferred deep link,
|
||||||
|
dynamic link, branch, uri scheme, custom scheme, url scheme]
|
||||||
|
pub_packages: [go_router, app_links, uni_links]
|
||||||
|
rules_to_load: [platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- URI parsed to correct route and parameters
|
||||||
|
- unknown URI falls back to home
|
||||||
|
- authenticated-only route redirects to login when unauthenticated
|
||||||
|
- deep link preserves query parameters
|
||||||
|
widget_test_scenarios:
|
||||||
|
- navigation guard redirects unauthenticated deep link
|
||||||
|
integration_test_scenarios:
|
||||||
|
- cold start from Universal Link routes to correct screen
|
||||||
|
- cold start from custom URI scheme routes correctly
|
||||||
|
- backgrounded app receives deep link and navigates
|
||||||
|
- foreground app receives deep link and navigates
|
||||||
|
- deep link to authenticated route redirects to login then original destination
|
||||||
|
- deep link with path parameters loads correct content
|
||||||
|
- invalid or malformed deep link shows 404 screen
|
||||||
|
external_setup:
|
||||||
|
ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/]
|
||||||
|
android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/]
|
||||||
|
modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist]
|
||||||
|
|
||||||
|
analytics:
|
||||||
|
keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics,
|
||||||
|
segment, posthog, screen view, funnel, cohort]
|
||||||
|
pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter]
|
||||||
|
rules_to_load: [security-standards.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- analytics service logs correct event name and params
|
||||||
|
- PII fields stripped before event sent
|
||||||
|
- analytics disabled in dev flavor
|
||||||
|
- screen name logged on navigation
|
||||||
|
widget_test_scenarios:
|
||||||
|
- RouteObserver triggers screen_view event
|
||||||
|
integration_test_scenarios:
|
||||||
|
- user action triggers expected event (verify via debug view)
|
||||||
|
- screen transitions log screen_view with correct names
|
||||||
|
- opt-out disables all tracking
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/]
|
||||||
|
modified: [lib/main.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
storage:
|
||||||
|
keywords: [storage, file upload, download, cloud storage, firebase storage,
|
||||||
|
supabase storage, s3, image upload, document, file picker,
|
||||||
|
offline, cache, hive, isar, objectbox, sqflite, drift]
|
||||||
|
pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- upload returns public URL on success
|
||||||
|
- upload failure returns typed error
|
||||||
|
- local cache read before remote fetch (offline-first)
|
||||||
|
- cache invalidation on TTL expiry
|
||||||
|
- large file upload uses resumable upload
|
||||||
|
widget_test_scenarios:
|
||||||
|
- file picker button triggers picker
|
||||||
|
- upload progress indicator shown
|
||||||
|
- image preview renders after selection
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end file upload and retrieval
|
||||||
|
- offline mode: local cache serves data
|
||||||
|
- background upload completes when connectivity restored
|
||||||
|
- file size limit enforced
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable]
|
||||||
|
ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist]
|
||||||
|
android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/storage/, integration_test/storage/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
|
||||||
|
camera_media:
|
||||||
|
keywords: [camera, photo, video, image picker, qr code, barcode scanner,
|
||||||
|
ar, augmented reality, gallery, media, record, capture]
|
||||||
|
pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- QR/barcode parsed to correct domain model
|
||||||
|
- image compressed before upload
|
||||||
|
- camera permission denied returns typed error
|
||||||
|
widget_test_scenarios:
|
||||||
|
- camera preview widget renders
|
||||||
|
- capture button triggers photo take
|
||||||
|
integration_test_scenarios:
|
||||||
|
- camera opens and captures photo on real device
|
||||||
|
- gallery picker selects image and returns
|
||||||
|
- QR scan decodes valid code correctly
|
||||||
|
- permission denied shows correct error UI
|
||||||
|
external_setup:
|
||||||
|
ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video]
|
||||||
|
android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/camera/, integration_test/camera/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Context Loading
|
||||||
|
|
||||||
|
**When the user types `/build <request>`, do the following before writing a single line of code:**
|
||||||
|
|
||||||
|
1. Read `project-brief.yaml` from the repo root. Extract and hold in context:
|
||||||
|
- `stack.state_management` → **bloc**
|
||||||
|
- `stack.architecture` → **clean**
|
||||||
|
- `stack.routing` → **gorouter**
|
||||||
|
- `stack.backend` → **firebase**
|
||||||
|
- `stack.platforms` → **ios, android**
|
||||||
|
- `stack.codegen` → **freezed**
|
||||||
|
- `testing.depth` → **unit_widget**
|
||||||
|
- `testing.e2e_tool` → **patrol**
|
||||||
|
- `features.modules` (existing features) → **auth, home, products**
|
||||||
|
- `features.special` → ****
|
||||||
|
- `environments.flavors` → **dev, prod**
|
||||||
|
- `environments.cicd` → **github_actions**
|
||||||
|
|
||||||
|
2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features.
|
||||||
|
|
||||||
|
3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type.
|
||||||
|
|
||||||
|
4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference.
|
||||||
|
|
||||||
|
5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)"
|
||||||
|
|
||||||
|
6. Print the context summary table before proceeding:
|
||||||
|
|
||||||
|
```
|
||||||
|
| Field | Value |
|
||||||
|
|------------------|------------------------------|
|
||||||
|
| Feature type | [detected type] |
|
||||||
|
| State management | BLoC / Cubit |
|
||||||
|
| Architecture | Clean Architecture |
|
||||||
|
| Backend | Firebase |
|
||||||
|
| Platforms | ios, android |
|
||||||
|
| E2E tool | patrol |
|
||||||
|
| Rules loaded | [list from rules_to_load] |
|
||||||
|
| Existing modules | auth, home, products |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Deep Research
|
||||||
|
|
||||||
|
**Before touching any file, print the full research output.**
|
||||||
|
|
||||||
|
1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions.
|
||||||
|
|
||||||
|
2. Build the complete file manifest, adapting to `Clean Architecture`:
|
||||||
|
|
||||||
|
**For Clean Architecture (clean):**
|
||||||
|
- `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/`
|
||||||
|
- `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/`
|
||||||
|
- `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/`
|
||||||
|
|
||||||
|
**State management file naming (BLoC / Cubit / bloc):**
|
||||||
|
- `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart`
|
||||||
|
- `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart`
|
||||||
|
- `getx`: `[feature]_controller.dart`, `[feature]_binding.dart`
|
||||||
|
|
||||||
|
3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file.
|
||||||
|
|
||||||
|
4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `ios, android`.
|
||||||
|
|
||||||
|
5. Print research results in this format:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Research Results
|
||||||
|
|
||||||
|
### Packages to add to pubspec.yaml
|
||||||
|
- [package_name]: ^[version]
|
||||||
|
|
||||||
|
### Files to create (Clean Architecture / BLoC / Cubit)
|
||||||
|
lib/features/[name]/... (full list)
|
||||||
|
|
||||||
|
### Files to modify
|
||||||
|
[file_path] — [what changes]
|
||||||
|
|
||||||
|
### External services requiring configuration
|
||||||
|
- [Service] ([platform]) — [what to do]
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not touch any file until the user has seen this output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: TDD Implementation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:test-driven-development`**
|
||||||
|
|
||||||
|
Follow Red → Green → Refactor strictly. Show actual terminal output at each step.
|
||||||
|
|
||||||
|
### Step 3a — Red: Write all failing tests first
|
||||||
|
|
||||||
|
1. Mirror the feature directory under `test/features/[feature]/`.
|
||||||
|
2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type.
|
||||||
|
3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`.
|
||||||
|
4. Use `mocktail` for all dependencies.
|
||||||
|
5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'`
|
||||||
|
6. Use the state management test pattern for **BLoC / Cubit**:
|
||||||
|
```
|
||||||
|
blocTest<MyCubit, MyState>(description, build: () => MyCubit(), act: (c) => c.method(), expect: () => [MyState()])
|
||||||
|
```
|
||||||
|
7. Run and confirm red:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3b — Green: Implement
|
||||||
|
|
||||||
|
1. Create domain entities and repository interfaces.
|
||||||
|
2. Create data-layer implementations wiring to `Firebase`.
|
||||||
|
3. Create presentation layer using **BLoC / Cubit** patterns.
|
||||||
|
4. Register in DI container — follow `- presentation/ MUST NOT import from data/
|
||||||
|
- domain/ MUST NOT import from data/ or presentation/
|
||||||
|
- data/ CAN import from domain/ (implements interfaces)
|
||||||
|
- Use dependency injection to invert data → domain dependency`.
|
||||||
|
5. Wire the route in `GoRouter` router.
|
||||||
|
6. If `freezed` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models.
|
||||||
|
7. Run tests and confirm green:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3c — Refactor
|
||||||
|
|
||||||
|
1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`.
|
||||||
|
2. Run and confirm clean:
|
||||||
|
```
|
||||||
|
flutter analyze
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
3. Run tests once more to confirm still green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Integration Test Generation
|
||||||
|
|
||||||
|
**Generate `integration_test/[feature_type]/` before asking the user to run anything.**
|
||||||
|
|
||||||
|
### Integration test file structure
|
||||||
|
|
||||||
|
Each test file follows this template:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// integration_test/[feature]/[scenario]_test.dart
|
||||||
|
// Generated by /build — TestApp
|
||||||
|
// Feature: [feature_type] | Scenario: [scenario_name]
|
||||||
|
// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d <device_id>
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:com.test.testapp/main.dart' as app;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
group('[FeatureType] — [Scenario Name]', () {
|
||||||
|
setUp(() async {
|
||||||
|
// Seed state / configure mocks / reset storage
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
// Cleanup
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'given [precondition], when [action], then [expected outcome]',
|
||||||
|
(tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
// test body
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**For `patrol` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax.
|
||||||
|
|
||||||
|
### Files to generate
|
||||||
|
|
||||||
|
Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`.
|
||||||
|
Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands.
|
||||||
|
|
||||||
|
### PAUSE GATE — user must run on device
|
||||||
|
|
||||||
|
After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6.
|
||||||
|
|
||||||
|
```
|
||||||
|
## ACTION REQUIRED — Run Integration Tests on Real Device
|
||||||
|
|
||||||
|
| File | Covers | Requires Hardware |
|
||||||
|
|------|--------|------------------|
|
||||||
|
| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No |
|
||||||
|
...
|
||||||
|
|
||||||
|
Run on iOS:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_ios_device_id>
|
||||||
|
|
||||||
|
Run on Android:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_android_device_id>
|
||||||
|
|
||||||
|
Paste the output here to continue.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: External Setup Checklist
|
||||||
|
|
||||||
|
**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `ios, android`.**
|
||||||
|
|
||||||
|
### Format for console/service steps (numbered list):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Firebase (backend: Firebase)
|
||||||
|
- [ ] 1. Open Firebase Console → [exact menu path]
|
||||||
|
- [ ] 2. [Specific action]
|
||||||
|
- [ ] 3. Download updated config file → place at [exact path]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format for native platform steps (table):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### iOS Setup
|
||||||
|
| Step | Where | What | How to Verify |
|
||||||
|
|------|-------|------|---------------|
|
||||||
|
| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] |
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Android Setup
|
||||||
|
- [ ] 1. Open `android/app/src/main/AndroidManifest.xml`
|
||||||
|
- Add: `<uses-permission android:name="[permission]"/>`
|
||||||
|
- [ ] 2. [Next step with exact value]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flavor scoping note (flavors: dev, prod)
|
||||||
|
|
||||||
|
Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Verification Gate
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:verification-before-completion`**
|
||||||
|
|
||||||
|
**Do not claim completion without pasting real output for every item below.**
|
||||||
|
|
||||||
|
```
|
||||||
|
VERIFICATION REQUIRED — paste real output for each:
|
||||||
|
|
||||||
|
[ ] flutter test test/features/[feature]/ --no-pub
|
||||||
|
Required: "All N tests passed."
|
||||||
|
|
||||||
|
[ ] flutter analyze
|
||||||
|
Required: "No issues found!"
|
||||||
|
|
||||||
|
[ ] Integration test device output (from Phase 4 pause gate)
|
||||||
|
Required: actual device test output
|
||||||
|
|
||||||
|
[ ] lefthook run pre-commit
|
||||||
|
Required: all hooks passed
|
||||||
|
```
|
||||||
|
|
||||||
|
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
|
||||||
|
Do not proceed to Phase 7 until all four checks are green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: PR Preparation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:finishing-a-development-branch`**
|
||||||
|
|
||||||
|
1. **Commit message** (conventional commits format):
|
||||||
|
```
|
||||||
|
feat([feature_type]): implement [feature] end-to-end
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **PR description template:**
|
||||||
|
```markdown
|
||||||
|
## What
|
||||||
|
[One sentence: what feature was implemented]
|
||||||
|
|
||||||
|
## Test coverage
|
||||||
|
- Unit/widget tests: N passing
|
||||||
|
- Integration test matrix:
|
||||||
|
| Scenario | iOS | Android |
|
||||||
|
|----------|-----|---------|
|
||||||
|
| [scenario] | ✅ | ✅ |
|
||||||
|
|
||||||
|
## External setup completed
|
||||||
|
- [ ] [Service 1 setup]
|
||||||
|
- [ ] [Service 2 setup]
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
[Any follow-up TODOs or known limitations]
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **CI/CD advice for GitHub Actions (github_actions):**
|
||||||
|
Ensure workflow secrets are scoped per flavor (dev, prod). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rules applied every phase
|
||||||
|
|
||||||
|
Always active — read before writing any code:
|
||||||
|
- `.cursor/rules/flutter-core.mdc`
|
||||||
|
- `.cursor/rules/security-standards.mdc`
|
||||||
|
- `.cursor/rules/project-context.mdc`
|
||||||
|
- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load`
|
||||||
|
|
||||||
|
Architecture import rules for **Clean Architecture**:
|
||||||
|
- presentation/ MUST NOT import from data/
|
||||||
|
- domain/ MUST NOT import from data/ or presentation/
|
||||||
|
- data/ CAN import from domain/ (implements interfaces)
|
||||||
|
- Use dependency injection to invert data → domain dependency
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code generation notes
|
||||||
|
|
||||||
|
**Codegen tools configured: freezed**
|
||||||
|
|
||||||
|
After adding any new model or injectable class, run:
|
||||||
|
```
|
||||||
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them.
|
||||||
|
|
||||||
|
**Template version:** 1.0.1
|
||||||
+572
@@ -0,0 +1,572 @@
|
|||||||
|
# Build — TaskFlow
|
||||||
|
|
||||||
|
Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR.
|
||||||
|
Stack: **Riverpod** / **Feature-First** / **Supabase** / ios, android, web.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/build <free-text description of what to implement>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- `/build implement notification module end-to-end`
|
||||||
|
- `/build add biometric auth`
|
||||||
|
- `/build integrate Stripe payments`
|
||||||
|
- `/build add deep linking for shared product pages`
|
||||||
|
- `/build implement analytics event tracking`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FEATURE_REGISTRY
|
||||||
|
|
||||||
|
The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
feature_registry:
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
keywords: [notification, push, fcm, apns, alert, badge, silent push,
|
||||||
|
remote notification, local notification, firebase messaging]
|
||||||
|
pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- parse notification payload to domain model
|
||||||
|
- FCM token generation and storage
|
||||||
|
- token refresh triggers re-registration
|
||||||
|
- notification permission denied returns graceful fallback
|
||||||
|
- notification service initialisation is idempotent
|
||||||
|
widget_test_scenarios:
|
||||||
|
- notification banner renders correct title and body
|
||||||
|
- tap on notification navigates to correct route
|
||||||
|
- badge count updates on new message
|
||||||
|
integration_test_scenarios:
|
||||||
|
- foreground notification receipt and display
|
||||||
|
- background notification receipt (app backgrounded, not killed)
|
||||||
|
- killed-state notification receipt (cold launch from notification tap)
|
||||||
|
- tap-to-open in foreground state routes correctly
|
||||||
|
- tap-to-open in background state routes correctly
|
||||||
|
- tap-to-open in killed state routes correctly
|
||||||
|
- notification payload parsing end-to-end
|
||||||
|
- deep link routing from notification data field
|
||||||
|
- multiple simultaneous notifications (ordering and dedup)
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist]
|
||||||
|
ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase]
|
||||||
|
android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/notifications/, integration_test/notifications/]
|
||||||
|
modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml]
|
||||||
|
|
||||||
|
auth:
|
||||||
|
keywords: [auth, authentication, login, sign in, sign out, logout,
|
||||||
|
biometric, face id, touch id, fingerprint, oauth, jwt,
|
||||||
|
session, token refresh, social login, google sign-in]
|
||||||
|
pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- login success stores token securely
|
||||||
|
- login failure (wrong password) returns typed error
|
||||||
|
- login failure (network) returns typed error
|
||||||
|
- token refresh succeeds and updates stored token
|
||||||
|
- token refresh failure triggers logout
|
||||||
|
- biometric auth success grants access
|
||||||
|
- biometric auth failure falls back to password
|
||||||
|
- biometric not enrolled returns correct error state
|
||||||
|
- logout clears all stored credentials
|
||||||
|
- session persistence: token loaded on cold start
|
||||||
|
widget_test_scenarios:
|
||||||
|
- login form renders correctly
|
||||||
|
- validation errors shown inline
|
||||||
|
- loading state disables submit button
|
||||||
|
- biometric prompt shown when available
|
||||||
|
integration_test_scenarios:
|
||||||
|
- complete login flow (email and password)
|
||||||
|
- login then logout then login again
|
||||||
|
- invalid credentials shows error and stays on login
|
||||||
|
- biometric login flow on enrolled device
|
||||||
|
- biometric fallback to password
|
||||||
|
- token refresh in background while user navigates
|
||||||
|
- cold start with stored valid session skips login
|
||||||
|
- cold start with expired session redirects to login
|
||||||
|
- social auth OAuth redirect and return
|
||||||
|
- deep link into protected route redirects to login then back
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Email/Password provider, Enable Google provider, download updated config files]
|
||||||
|
ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode]
|
||||||
|
android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/auth/, integration_test/auth/]
|
||||||
|
modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
payments:
|
||||||
|
keywords: [payment, stripe, in-app purchase, iap, checkout, subscription,
|
||||||
|
billing, apple pay, google pay, card, transaction, refund, revenue cat]
|
||||||
|
pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- checkout request builds correct PaymentIntent params
|
||||||
|
- payment success updates order state
|
||||||
|
- payment failure (card declined) returns typed error
|
||||||
|
- webhook event parsed to domain model
|
||||||
|
- refund request constructs correct API call
|
||||||
|
- subscription status checked on app resume
|
||||||
|
widget_test_scenarios:
|
||||||
|
- checkout form renders with correct amount
|
||||||
|
- payment loading state shown during processing
|
||||||
|
- success confirmation screen renders
|
||||||
|
- error state with retry option
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end checkout with test card (Stripe test mode)
|
||||||
|
- checkout with 3D Secure challenge
|
||||||
|
- card declined shows error then retry succeeds
|
||||||
|
- Apple Pay / Google Pay sheet appears (device capability check)
|
||||||
|
- subscription purchase and entitlement unlock
|
||||||
|
- subscription restore flow
|
||||||
|
- refund flow from order history
|
||||||
|
external_setup:
|
||||||
|
stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration]
|
||||||
|
ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement]
|
||||||
|
android: [Google Pay Console setup, BILLING permission for in_app_purchase]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/payments/, integration_test/payments/]
|
||||||
|
modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml]
|
||||||
|
|
||||||
|
deep_links:
|
||||||
|
keywords: [deep link, deep linking, universal link, app link, deferred deep link,
|
||||||
|
dynamic link, branch, uri scheme, custom scheme, url scheme]
|
||||||
|
pub_packages: [go_router, app_links, uni_links]
|
||||||
|
rules_to_load: [platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- URI parsed to correct route and parameters
|
||||||
|
- unknown URI falls back to home
|
||||||
|
- authenticated-only route redirects to login when unauthenticated
|
||||||
|
- deep link preserves query parameters
|
||||||
|
widget_test_scenarios:
|
||||||
|
- navigation guard redirects unauthenticated deep link
|
||||||
|
integration_test_scenarios:
|
||||||
|
- cold start from Universal Link routes to correct screen
|
||||||
|
- cold start from custom URI scheme routes correctly
|
||||||
|
- backgrounded app receives deep link and navigates
|
||||||
|
- foreground app receives deep link and navigates
|
||||||
|
- deep link to authenticated route redirects to login then original destination
|
||||||
|
- deep link with path parameters loads correct content
|
||||||
|
- invalid or malformed deep link shows 404 screen
|
||||||
|
external_setup:
|
||||||
|
ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/]
|
||||||
|
android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/]
|
||||||
|
modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist]
|
||||||
|
|
||||||
|
analytics:
|
||||||
|
keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics,
|
||||||
|
segment, posthog, screen view, funnel, cohort]
|
||||||
|
pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter]
|
||||||
|
rules_to_load: [security-standards.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- analytics service logs correct event name and params
|
||||||
|
- PII fields stripped before event sent
|
||||||
|
- analytics disabled in dev flavor
|
||||||
|
- screen name logged on navigation
|
||||||
|
widget_test_scenarios:
|
||||||
|
- RouteObserver triggers screen_view event
|
||||||
|
integration_test_scenarios:
|
||||||
|
- user action triggers expected event (verify via debug view)
|
||||||
|
- screen transitions log screen_view with correct names
|
||||||
|
- opt-out disables all tracking
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/]
|
||||||
|
modified: [lib/main.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
storage:
|
||||||
|
keywords: [storage, file upload, download, cloud storage, firebase storage,
|
||||||
|
supabase storage, s3, image upload, document, file picker,
|
||||||
|
offline, cache, hive, isar, objectbox, sqflite, drift]
|
||||||
|
pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- upload returns public URL on success
|
||||||
|
- upload failure returns typed error
|
||||||
|
- local cache read before remote fetch (offline-first)
|
||||||
|
- cache invalidation on TTL expiry
|
||||||
|
- large file upload uses resumable upload
|
||||||
|
widget_test_scenarios:
|
||||||
|
- file picker button triggers picker
|
||||||
|
- upload progress indicator shown
|
||||||
|
- image preview renders after selection
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end file upload and retrieval
|
||||||
|
- offline mode: local cache serves data
|
||||||
|
- background upload completes when connectivity restored
|
||||||
|
- file size limit enforced
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable]
|
||||||
|
ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist]
|
||||||
|
android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/storage/, integration_test/storage/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
|
||||||
|
camera_media:
|
||||||
|
keywords: [camera, photo, video, image picker, qr code, barcode scanner,
|
||||||
|
ar, augmented reality, gallery, media, record, capture]
|
||||||
|
pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- QR/barcode parsed to correct domain model
|
||||||
|
- image compressed before upload
|
||||||
|
- camera permission denied returns typed error
|
||||||
|
widget_test_scenarios:
|
||||||
|
- camera preview widget renders
|
||||||
|
- capture button triggers photo take
|
||||||
|
integration_test_scenarios:
|
||||||
|
- camera opens and captures photo on real device
|
||||||
|
- gallery picker selects image and returns
|
||||||
|
- QR scan decodes valid code correctly
|
||||||
|
- permission denied shows correct error UI
|
||||||
|
external_setup:
|
||||||
|
ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video]
|
||||||
|
android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/camera/, integration_test/camera/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Context Loading
|
||||||
|
|
||||||
|
**When the user types `/build <request>`, do the following before writing a single line of code:**
|
||||||
|
|
||||||
|
1. Read `project-brief.yaml` from the repo root. Extract and hold in context:
|
||||||
|
- `stack.state_management` → **riverpod**
|
||||||
|
- `stack.architecture` → **feature_first**
|
||||||
|
- `stack.routing` → **gorouter**
|
||||||
|
- `stack.backend` → **supabase**
|
||||||
|
- `stack.platforms` → **ios, android, web**
|
||||||
|
- `stack.codegen` → **freezed, json_serializable**
|
||||||
|
- `testing.depth` → **unit_widget**
|
||||||
|
- `testing.e2e_tool` → **patrol**
|
||||||
|
- `features.modules` (existing features) → **auth, tasks, profile**
|
||||||
|
- `features.special` → ****
|
||||||
|
- `environments.flavors` → **dev, prod**
|
||||||
|
- `environments.cicd` → **github_actions**
|
||||||
|
|
||||||
|
2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features.
|
||||||
|
|
||||||
|
3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type.
|
||||||
|
|
||||||
|
4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference.
|
||||||
|
|
||||||
|
5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)"
|
||||||
|
|
||||||
|
6. Print the context summary table before proceeding:
|
||||||
|
|
||||||
|
```
|
||||||
|
| Field | Value |
|
||||||
|
|------------------|------------------------------|
|
||||||
|
| Feature type | [detected type] |
|
||||||
|
| State management | Riverpod |
|
||||||
|
| Architecture | Feature-First |
|
||||||
|
| Backend | Supabase |
|
||||||
|
| Platforms | ios, android, web |
|
||||||
|
| E2E tool | patrol |
|
||||||
|
| Rules loaded | [list from rules_to_load] |
|
||||||
|
| Existing modules | auth, tasks, profile |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Deep Research
|
||||||
|
|
||||||
|
**Before touching any file, print the full research output.**
|
||||||
|
|
||||||
|
1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions.
|
||||||
|
|
||||||
|
2. Build the complete file manifest, adapting to `Feature-First`:
|
||||||
|
|
||||||
|
**For Feature-First (feature_first):**
|
||||||
|
- `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/`
|
||||||
|
- `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/`
|
||||||
|
- `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/`
|
||||||
|
|
||||||
|
**State management file naming (Riverpod / riverpod):**
|
||||||
|
- `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart`
|
||||||
|
- `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart`
|
||||||
|
- `getx`: `[feature]_controller.dart`, `[feature]_binding.dart`
|
||||||
|
|
||||||
|
3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file.
|
||||||
|
|
||||||
|
4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `ios, android, web`.
|
||||||
|
|
||||||
|
5. Print research results in this format:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Research Results
|
||||||
|
|
||||||
|
### Packages to add to pubspec.yaml
|
||||||
|
- [package_name]: ^[version]
|
||||||
|
|
||||||
|
### Files to create (Feature-First / Riverpod)
|
||||||
|
lib/features/[name]/... (full list)
|
||||||
|
|
||||||
|
### Files to modify
|
||||||
|
[file_path] — [what changes]
|
||||||
|
|
||||||
|
### External services requiring configuration
|
||||||
|
- [Service] ([platform]) — [what to do]
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not touch any file until the user has seen this output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: TDD Implementation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:test-driven-development`**
|
||||||
|
|
||||||
|
Follow Red → Green → Refactor strictly. Show actual terminal output at each step.
|
||||||
|
|
||||||
|
### Step 3a — Red: Write all failing tests first
|
||||||
|
|
||||||
|
1. Mirror the feature directory under `test/features/[feature]/`.
|
||||||
|
2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type.
|
||||||
|
3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`.
|
||||||
|
4. Use `mocktail` for all dependencies.
|
||||||
|
5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'`
|
||||||
|
6. Use the state management test pattern for **Riverpod**:
|
||||||
|
```
|
||||||
|
final container = ProviderContainer(overrides: [myProvider.overrideWithValue(mockValue)]); addTearDown(container.dispose);
|
||||||
|
```
|
||||||
|
7. Run and confirm red:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3b — Green: Implement
|
||||||
|
|
||||||
|
1. Create domain entities and repository interfaces.
|
||||||
|
2. Create data-layer implementations wiring to `Supabase`.
|
||||||
|
3. Create presentation layer using **Riverpod** patterns.
|
||||||
|
4. Register in DI container — follow `- features/auth/ MUST NOT import from features/home/ etc.
|
||||||
|
- Shared code lives in core/ or shared/ only
|
||||||
|
- Each feature is self-contained: screen + provider + model + repo`.
|
||||||
|
5. Wire the route in `GoRouter` router.
|
||||||
|
6. If `freezed, json_serializable` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models.
|
||||||
|
7. Run tests and confirm green:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3c — Refactor
|
||||||
|
|
||||||
|
1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`.
|
||||||
|
2. Run and confirm clean:
|
||||||
|
```
|
||||||
|
flutter analyze
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
3. Run tests once more to confirm still green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Integration Test Generation
|
||||||
|
|
||||||
|
**Generate `integration_test/[feature_type]/` before asking the user to run anything.**
|
||||||
|
|
||||||
|
### Integration test file structure
|
||||||
|
|
||||||
|
Each test file follows this template:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// integration_test/[feature]/[scenario]_test.dart
|
||||||
|
// Generated by /build — TaskFlow
|
||||||
|
// Feature: [feature_type] | Scenario: [scenario_name]
|
||||||
|
// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d <device_id>
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:com.test.taskflow/main.dart' as app;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
group('[FeatureType] — [Scenario Name]', () {
|
||||||
|
setUp(() async {
|
||||||
|
// Seed state / configure mocks / reset storage
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
// Cleanup
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'given [precondition], when [action], then [expected outcome]',
|
||||||
|
(tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
// test body
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**For `patrol` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax.
|
||||||
|
|
||||||
|
### Files to generate
|
||||||
|
|
||||||
|
Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`.
|
||||||
|
Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands.
|
||||||
|
|
||||||
|
### PAUSE GATE — user must run on device
|
||||||
|
|
||||||
|
After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6.
|
||||||
|
|
||||||
|
```
|
||||||
|
## ACTION REQUIRED — Run Integration Tests on Real Device
|
||||||
|
|
||||||
|
| File | Covers | Requires Hardware |
|
||||||
|
|------|--------|------------------|
|
||||||
|
| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No |
|
||||||
|
...
|
||||||
|
|
||||||
|
Run on iOS:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_ios_device_id>
|
||||||
|
|
||||||
|
Run on Android:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_android_device_id>
|
||||||
|
|
||||||
|
Paste the output here to continue.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: External Setup Checklist
|
||||||
|
|
||||||
|
**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `ios, android, web`.**
|
||||||
|
|
||||||
|
### Format for console/service steps (numbered list):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Firebase (backend: Supabase)
|
||||||
|
- [ ] 1. Open Firebase Console → [exact menu path]
|
||||||
|
- [ ] 2. [Specific action]
|
||||||
|
- [ ] 3. Download updated config file → place at [exact path]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format for native platform steps (table):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### iOS Setup
|
||||||
|
| Step | Where | What | How to Verify |
|
||||||
|
|------|-------|------|---------------|
|
||||||
|
| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] |
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Android Setup
|
||||||
|
- [ ] 1. Open `android/app/src/main/AndroidManifest.xml`
|
||||||
|
- Add: `<uses-permission android:name="[permission]"/>`
|
||||||
|
- [ ] 2. [Next step with exact value]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flavor scoping note (flavors: dev, prod)
|
||||||
|
|
||||||
|
Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Verification Gate
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:verification-before-completion`**
|
||||||
|
|
||||||
|
**Do not claim completion without pasting real output for every item below.**
|
||||||
|
|
||||||
|
```
|
||||||
|
VERIFICATION REQUIRED — paste real output for each:
|
||||||
|
|
||||||
|
[ ] flutter test test/features/[feature]/ --no-pub
|
||||||
|
Required: "All N tests passed."
|
||||||
|
|
||||||
|
[ ] flutter analyze
|
||||||
|
Required: "No issues found!"
|
||||||
|
|
||||||
|
[ ] Integration test device output (from Phase 4 pause gate)
|
||||||
|
Required: actual device test output
|
||||||
|
|
||||||
|
[ ] lefthook run pre-commit
|
||||||
|
Required: all hooks passed
|
||||||
|
```
|
||||||
|
|
||||||
|
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
|
||||||
|
Do not proceed to Phase 7 until all four checks are green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: PR Preparation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:finishing-a-development-branch`**
|
||||||
|
|
||||||
|
1. **Commit message** (conventional commits format):
|
||||||
|
```
|
||||||
|
feat([feature_type]): implement [feature] end-to-end
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **PR description template:**
|
||||||
|
```markdown
|
||||||
|
## What
|
||||||
|
[One sentence: what feature was implemented]
|
||||||
|
|
||||||
|
## Test coverage
|
||||||
|
- Unit/widget tests: N passing
|
||||||
|
- Integration test matrix:
|
||||||
|
| Scenario | iOS | Android |
|
||||||
|
|----------|-----|---------|
|
||||||
|
| [scenario] | ✅ | ✅ |
|
||||||
|
|
||||||
|
## External setup completed
|
||||||
|
- [ ] [Service 1 setup]
|
||||||
|
- [ ] [Service 2 setup]
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
[Any follow-up TODOs or known limitations]
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **CI/CD advice for GitHub Actions (github_actions):**
|
||||||
|
Ensure workflow secrets are scoped per flavor (dev, prod). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rules applied every phase
|
||||||
|
|
||||||
|
Always active — read before writing any code:
|
||||||
|
- `.cursor/rules/flutter-core.mdc`
|
||||||
|
- `.cursor/rules/security-standards.mdc`
|
||||||
|
- `.cursor/rules/project-context.mdc`
|
||||||
|
- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load`
|
||||||
|
|
||||||
|
Architecture import rules for **Feature-First**:
|
||||||
|
- features/auth/ MUST NOT import from features/home/ etc.
|
||||||
|
- Shared code lives in core/ or shared/ only
|
||||||
|
- Each feature is self-contained: screen + provider + model + repo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code generation notes
|
||||||
|
|
||||||
|
**Codegen tools configured: freezed, json_serializable**
|
||||||
|
|
||||||
|
After adding any new model or injectable class, run:
|
||||||
|
```
|
||||||
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them.
|
||||||
|
|
||||||
|
**Template version:** 1.0.1
|
||||||
@@ -0,0 +1,568 @@
|
|||||||
|
# Build — {{PROJECT_NAME}}
|
||||||
|
|
||||||
|
Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR.
|
||||||
|
Stack: **{{STATE_MANAGEMENT}}** / **{{ARCHITECTURE}}** / **{{BACKEND}}** / {{PLATFORMS_LIST}}.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/build <free-text description of what to implement>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- `/build implement notification module end-to-end`
|
||||||
|
- `/build add biometric auth`
|
||||||
|
- `/build integrate Stripe payments`
|
||||||
|
- `/build add deep linking for shared product pages`
|
||||||
|
- `/build implement analytics event tracking`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FEATURE_REGISTRY
|
||||||
|
|
||||||
|
The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
feature_registry:
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
keywords: [notification, push, fcm, apns, alert, badge, silent push,
|
||||||
|
remote notification, local notification, firebase messaging]
|
||||||
|
pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- parse notification payload to domain model
|
||||||
|
- FCM token generation and storage
|
||||||
|
- token refresh triggers re-registration
|
||||||
|
- notification permission denied returns graceful fallback
|
||||||
|
- notification service initialisation is idempotent
|
||||||
|
widget_test_scenarios:
|
||||||
|
- notification banner renders correct title and body
|
||||||
|
- tap on notification navigates to correct route
|
||||||
|
- badge count updates on new message
|
||||||
|
integration_test_scenarios:
|
||||||
|
- foreground notification receipt and display
|
||||||
|
- background notification receipt (app backgrounded, not killed)
|
||||||
|
- killed-state notification receipt (cold launch from notification tap)
|
||||||
|
- tap-to-open in foreground state routes correctly
|
||||||
|
- tap-to-open in background state routes correctly
|
||||||
|
- tap-to-open in killed state routes correctly
|
||||||
|
- notification payload parsing end-to-end
|
||||||
|
- deep link routing from notification data field
|
||||||
|
- multiple simultaneous notifications (ordering and dedup)
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist]
|
||||||
|
ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase]
|
||||||
|
android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/notifications/, integration_test/notifications/]
|
||||||
|
modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml]
|
||||||
|
|
||||||
|
auth:
|
||||||
|
keywords: [auth, authentication, login, sign in, sign out, logout,
|
||||||
|
biometric, face id, touch id, fingerprint, oauth, jwt,
|
||||||
|
session, token refresh, social login, google sign-in]
|
||||||
|
pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- login success stores token securely
|
||||||
|
- login failure (wrong password) returns typed error
|
||||||
|
- login failure (network) returns typed error
|
||||||
|
- token refresh succeeds and updates stored token
|
||||||
|
- token refresh failure triggers logout
|
||||||
|
- biometric auth success grants access
|
||||||
|
- biometric auth failure falls back to password
|
||||||
|
- biometric not enrolled returns correct error state
|
||||||
|
- logout clears all stored credentials
|
||||||
|
- session persistence: token loaded on cold start
|
||||||
|
widget_test_scenarios:
|
||||||
|
- login form renders correctly
|
||||||
|
- validation errors shown inline
|
||||||
|
- loading state disables submit button
|
||||||
|
- biometric prompt shown when available
|
||||||
|
integration_test_scenarios:
|
||||||
|
- complete login flow (email and password)
|
||||||
|
- login then logout then login again
|
||||||
|
- invalid credentials shows error and stays on login
|
||||||
|
- biometric login flow on enrolled device
|
||||||
|
- biometric fallback to password
|
||||||
|
- token refresh in background while user navigates
|
||||||
|
- cold start with stored valid session skips login
|
||||||
|
- cold start with expired session redirects to login
|
||||||
|
- social auth OAuth redirect and return
|
||||||
|
- deep link into protected route redirects to login then back
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Email/Password provider, Enable Google provider, download updated config files]
|
||||||
|
ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode]
|
||||||
|
android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/auth/, integration_test/auth/]
|
||||||
|
modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
payments:
|
||||||
|
keywords: [payment, stripe, in-app purchase, iap, checkout, subscription,
|
||||||
|
billing, apple pay, google pay, card, transaction, refund, revenue cat]
|
||||||
|
pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- checkout request builds correct PaymentIntent params
|
||||||
|
- payment success updates order state
|
||||||
|
- payment failure (card declined) returns typed error
|
||||||
|
- webhook event parsed to domain model
|
||||||
|
- refund request constructs correct API call
|
||||||
|
- subscription status checked on app resume
|
||||||
|
widget_test_scenarios:
|
||||||
|
- checkout form renders with correct amount
|
||||||
|
- payment loading state shown during processing
|
||||||
|
- success confirmation screen renders
|
||||||
|
- error state with retry option
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end checkout with test card (Stripe test mode)
|
||||||
|
- checkout with 3D Secure challenge
|
||||||
|
- card declined shows error then retry succeeds
|
||||||
|
- Apple Pay / Google Pay sheet appears (device capability check)
|
||||||
|
- subscription purchase and entitlement unlock
|
||||||
|
- subscription restore flow
|
||||||
|
- refund flow from order history
|
||||||
|
external_setup:
|
||||||
|
stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration]
|
||||||
|
ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement]
|
||||||
|
android: [Google Pay Console setup, BILLING permission for in_app_purchase]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/payments/, integration_test/payments/]
|
||||||
|
modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml]
|
||||||
|
|
||||||
|
deep_links:
|
||||||
|
keywords: [deep link, deep linking, universal link, app link, deferred deep link,
|
||||||
|
dynamic link, branch, uri scheme, custom scheme, url scheme]
|
||||||
|
pub_packages: [go_router, app_links, uni_links]
|
||||||
|
rules_to_load: [platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- URI parsed to correct route and parameters
|
||||||
|
- unknown URI falls back to home
|
||||||
|
- authenticated-only route redirects to login when unauthenticated
|
||||||
|
- deep link preserves query parameters
|
||||||
|
widget_test_scenarios:
|
||||||
|
- navigation guard redirects unauthenticated deep link
|
||||||
|
integration_test_scenarios:
|
||||||
|
- cold start from Universal Link routes to correct screen
|
||||||
|
- cold start from custom URI scheme routes correctly
|
||||||
|
- backgrounded app receives deep link and navigates
|
||||||
|
- foreground app receives deep link and navigates
|
||||||
|
- deep link to authenticated route redirects to login then original destination
|
||||||
|
- deep link with path parameters loads correct content
|
||||||
|
- invalid or malformed deep link shows 404 screen
|
||||||
|
external_setup:
|
||||||
|
ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/]
|
||||||
|
android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/]
|
||||||
|
modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist]
|
||||||
|
|
||||||
|
analytics:
|
||||||
|
keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics,
|
||||||
|
segment, posthog, screen view, funnel, cohort]
|
||||||
|
pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter]
|
||||||
|
rules_to_load: [security-standards.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- analytics service logs correct event name and params
|
||||||
|
- PII fields stripped before event sent
|
||||||
|
- analytics disabled in dev flavor
|
||||||
|
- screen name logged on navigation
|
||||||
|
widget_test_scenarios:
|
||||||
|
- RouteObserver triggers screen_view event
|
||||||
|
integration_test_scenarios:
|
||||||
|
- user action triggers expected event (verify via debug view)
|
||||||
|
- screen transitions log screen_view with correct names
|
||||||
|
- opt-out disables all tracking
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/]
|
||||||
|
modified: [lib/main.dart, pubspec.yaml]
|
||||||
|
|
||||||
|
storage:
|
||||||
|
keywords: [storage, file upload, download, cloud storage, firebase storage,
|
||||||
|
supabase storage, s3, image upload, document, file picker,
|
||||||
|
offline, cache, hive, isar, objectbox, sqflite, drift]
|
||||||
|
pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- upload returns public URL on success
|
||||||
|
- upload failure returns typed error
|
||||||
|
- local cache read before remote fetch (offline-first)
|
||||||
|
- cache invalidation on TTL expiry
|
||||||
|
- large file upload uses resumable upload
|
||||||
|
widget_test_scenarios:
|
||||||
|
- file picker button triggers picker
|
||||||
|
- upload progress indicator shown
|
||||||
|
- image preview renders after selection
|
||||||
|
integration_test_scenarios:
|
||||||
|
- end-to-end file upload and retrieval
|
||||||
|
- offline mode: local cache serves data
|
||||||
|
- background upload completes when connectivity restored
|
||||||
|
- file size limit enforced
|
||||||
|
external_setup:
|
||||||
|
firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable]
|
||||||
|
ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist]
|
||||||
|
android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/storage/, integration_test/storage/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
|
||||||
|
camera_media:
|
||||||
|
keywords: [camera, photo, video, image picker, qr code, barcode scanner,
|
||||||
|
ar, augmented reality, gallery, media, record, capture]
|
||||||
|
pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player]
|
||||||
|
rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc]
|
||||||
|
unit_test_scenarios:
|
||||||
|
- QR/barcode parsed to correct domain model
|
||||||
|
- image compressed before upload
|
||||||
|
- camera permission denied returns typed error
|
||||||
|
widget_test_scenarios:
|
||||||
|
- camera preview widget renders
|
||||||
|
- capture button triggers photo take
|
||||||
|
integration_test_scenarios:
|
||||||
|
- camera opens and captures photo on real device
|
||||||
|
- gallery picker selects image and returns
|
||||||
|
- QR scan decodes valid code correctly
|
||||||
|
- permission denied shows correct error UI
|
||||||
|
external_setup:
|
||||||
|
ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video]
|
||||||
|
android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access]
|
||||||
|
files_to_touch:
|
||||||
|
new: [lib/features/camera/, integration_test/camera/]
|
||||||
|
modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Context Loading
|
||||||
|
|
||||||
|
**When the user types `/build <request>`, do the following before writing a single line of code:**
|
||||||
|
|
||||||
|
1. Read `project-brief.yaml` from the repo root. Extract and hold in context:
|
||||||
|
- `stack.state_management` → **{{STATE_MGMT_RAW}}**
|
||||||
|
- `stack.architecture` → **{{ARCH_RAW}}**
|
||||||
|
- `stack.routing` → **{{ROUTING_RAW}}**
|
||||||
|
- `stack.backend` → **{{BACKENDS_LIST}}**
|
||||||
|
- `stack.platforms` → **{{PLATFORMS_LIST}}**
|
||||||
|
- `stack.codegen` → **{{CODEGEN_LIST}}**
|
||||||
|
- `testing.depth` → **{{TESTING_DEPTH}}**
|
||||||
|
- `testing.e2e_tool` → **{{E2E_TOOL}}**
|
||||||
|
- `features.modules` (existing features) → **{{FEATURES_LIST}}**
|
||||||
|
- `features.special` → **{{SPECIAL_FEATURES}}**
|
||||||
|
- `environments.flavors` → **{{FLAVORS_LIST}}**
|
||||||
|
- `environments.cicd` → **{{CICD_RAW}}**
|
||||||
|
|
||||||
|
2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features.
|
||||||
|
|
||||||
|
3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type.
|
||||||
|
|
||||||
|
4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference.
|
||||||
|
|
||||||
|
5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)"
|
||||||
|
|
||||||
|
6. Print the context summary table before proceeding:
|
||||||
|
|
||||||
|
```
|
||||||
|
| Field | Value |
|
||||||
|
|------------------|------------------------------|
|
||||||
|
| Feature type | [detected type] |
|
||||||
|
| State management | {{STATE_MANAGEMENT}} |
|
||||||
|
| Architecture | {{ARCHITECTURE}} |
|
||||||
|
| Backend | {{BACKEND}} |
|
||||||
|
| Platforms | {{PLATFORMS_LIST}} |
|
||||||
|
| E2E tool | {{E2E_TOOL}} |
|
||||||
|
| Rules loaded | [list from rules_to_load] |
|
||||||
|
| Existing modules | {{FEATURES_LIST}} |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Deep Research
|
||||||
|
|
||||||
|
**Before touching any file, print the full research output.**
|
||||||
|
|
||||||
|
1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions.
|
||||||
|
|
||||||
|
2. Build the complete file manifest, adapting to `{{ARCHITECTURE}}`:
|
||||||
|
|
||||||
|
**For {{ARCHITECTURE}} ({{ARCH_RAW}}):**
|
||||||
|
- `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/`
|
||||||
|
- `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/`
|
||||||
|
- `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/`
|
||||||
|
|
||||||
|
**State management file naming ({{STATE_MANAGEMENT}} / {{STATE_MGMT_RAW}}):**
|
||||||
|
- `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart`
|
||||||
|
- `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart`
|
||||||
|
- `getx`: `[feature]_controller.dart`, `[feature]_binding.dart`
|
||||||
|
|
||||||
|
3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file.
|
||||||
|
|
||||||
|
4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `{{PLATFORMS_LIST}}`.
|
||||||
|
|
||||||
|
5. Print research results in this format:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Research Results
|
||||||
|
|
||||||
|
### Packages to add to pubspec.yaml
|
||||||
|
- [package_name]: ^[version]
|
||||||
|
|
||||||
|
### Files to create ({{ARCHITECTURE}} / {{STATE_MANAGEMENT}})
|
||||||
|
lib/features/[name]/... (full list)
|
||||||
|
|
||||||
|
### Files to modify
|
||||||
|
[file_path] — [what changes]
|
||||||
|
|
||||||
|
### External services requiring configuration
|
||||||
|
- [Service] ([platform]) — [what to do]
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not touch any file until the user has seen this output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: TDD Implementation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:test-driven-development`**
|
||||||
|
|
||||||
|
Follow Red → Green → Refactor strictly. Show actual terminal output at each step.
|
||||||
|
|
||||||
|
### Step 3a — Red: Write all failing tests first
|
||||||
|
|
||||||
|
1. Mirror the feature directory under `test/features/[feature]/`.
|
||||||
|
2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type.
|
||||||
|
3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`.
|
||||||
|
4. Use `mocktail` for all dependencies.
|
||||||
|
5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'`
|
||||||
|
6. Use the state management test pattern for **{{STATE_MANAGEMENT}}**:
|
||||||
|
```
|
||||||
|
{{TEST_PATTERN}}
|
||||||
|
```
|
||||||
|
7. Run and confirm red:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3b — Green: Implement
|
||||||
|
|
||||||
|
1. Create domain entities and repository interfaces.
|
||||||
|
2. Create data-layer implementations wiring to `{{BACKEND}}`.
|
||||||
|
3. Create presentation layer using **{{STATE_MANAGEMENT}}** patterns.
|
||||||
|
4. Register in DI container — follow `{{ARCH_IMPORT_RULES}}`.
|
||||||
|
5. Wire the route in `{{ROUTING}}` router.
|
||||||
|
6. If `{{CODEGEN_LIST}}` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models.
|
||||||
|
7. Run tests and confirm green:
|
||||||
|
```
|
||||||
|
flutter test test/features/[feature]/ --no-pub
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
|
||||||
|
### Step 3c — Refactor
|
||||||
|
|
||||||
|
1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`.
|
||||||
|
2. Run and confirm clean:
|
||||||
|
```
|
||||||
|
flutter analyze
|
||||||
|
```
|
||||||
|
**Paste the actual output here before proceeding.**
|
||||||
|
3. Run tests once more to confirm still green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Integration Test Generation
|
||||||
|
|
||||||
|
**Generate `integration_test/[feature_type]/` before asking the user to run anything.**
|
||||||
|
|
||||||
|
### Integration test file structure
|
||||||
|
|
||||||
|
Each test file follows this template:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// integration_test/[feature]/[scenario]_test.dart
|
||||||
|
// Generated by /build — {{PROJECT_NAME}}
|
||||||
|
// Feature: [feature_type] | Scenario: [scenario_name]
|
||||||
|
// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d <device_id>
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:{{PACKAGE_ID}}/main.dart' as app;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
group('[FeatureType] — [Scenario Name]', () {
|
||||||
|
setUp(() async {
|
||||||
|
// Seed state / configure mocks / reset storage
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
// Cleanup
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'given [precondition], when [action], then [expected outcome]',
|
||||||
|
(tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
// test body
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**For `{{E2E_TOOL}}` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax.
|
||||||
|
|
||||||
|
### Files to generate
|
||||||
|
|
||||||
|
Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`.
|
||||||
|
Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands.
|
||||||
|
|
||||||
|
### PAUSE GATE — user must run on device
|
||||||
|
|
||||||
|
After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6.
|
||||||
|
|
||||||
|
```
|
||||||
|
## ACTION REQUIRED — Run Integration Tests on Real Device
|
||||||
|
|
||||||
|
| File | Covers | Requires Hardware |
|
||||||
|
|------|--------|------------------|
|
||||||
|
| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No |
|
||||||
|
...
|
||||||
|
|
||||||
|
Run on iOS:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_ios_device_id>
|
||||||
|
|
||||||
|
Run on Android:
|
||||||
|
flutter test integration_test/[feature]/ -d <your_android_device_id>
|
||||||
|
|
||||||
|
Paste the output here to continue.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: External Setup Checklist
|
||||||
|
|
||||||
|
**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `{{PLATFORMS_LIST}}`.**
|
||||||
|
|
||||||
|
### Format for console/service steps (numbered list):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Firebase (backend: {{BACKEND}})
|
||||||
|
- [ ] 1. Open Firebase Console → [exact menu path]
|
||||||
|
- [ ] 2. [Specific action]
|
||||||
|
- [ ] 3. Download updated config file → place at [exact path]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format for native platform steps (table):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### iOS Setup
|
||||||
|
| Step | Where | What | How to Verify |
|
||||||
|
|------|-------|------|---------------|
|
||||||
|
| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] |
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Android Setup
|
||||||
|
- [ ] 1. Open `android/app/src/main/AndroidManifest.xml`
|
||||||
|
- Add: `<uses-permission android:name="[permission]"/>`
|
||||||
|
- [ ] 2. [Next step with exact value]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flavor scoping note (flavors: {{FLAVORS_LIST}})
|
||||||
|
|
||||||
|
Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Verification Gate
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:verification-before-completion`**
|
||||||
|
|
||||||
|
**Do not claim completion without pasting real output for every item below.**
|
||||||
|
|
||||||
|
```
|
||||||
|
VERIFICATION REQUIRED — paste real output for each:
|
||||||
|
|
||||||
|
[ ] flutter test test/features/[feature]/ --no-pub
|
||||||
|
Required: "All N tests passed."
|
||||||
|
|
||||||
|
[ ] flutter analyze
|
||||||
|
Required: "No issues found!"
|
||||||
|
|
||||||
|
[ ] Integration test device output (from Phase 4 pause gate)
|
||||||
|
Required: actual device test output
|
||||||
|
|
||||||
|
[ ] lefthook run pre-commit
|
||||||
|
Required: all hooks passed
|
||||||
|
```
|
||||||
|
|
||||||
|
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
|
||||||
|
Do not proceed to Phase 7 until all four checks are green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: PR Preparation
|
||||||
|
|
||||||
|
> **Invoke skill: `superpowers:finishing-a-development-branch`**
|
||||||
|
|
||||||
|
1. **Commit message** (conventional commits format):
|
||||||
|
```
|
||||||
|
feat([feature_type]): implement [feature] end-to-end
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **PR description template:**
|
||||||
|
```markdown
|
||||||
|
## What
|
||||||
|
[One sentence: what feature was implemented]
|
||||||
|
|
||||||
|
## Test coverage
|
||||||
|
- Unit/widget tests: N passing
|
||||||
|
- Integration test matrix:
|
||||||
|
| Scenario | iOS | Android |
|
||||||
|
|----------|-----|---------|
|
||||||
|
| [scenario] | ✅ | ✅ |
|
||||||
|
|
||||||
|
## External setup completed
|
||||||
|
- [ ] [Service 1 setup]
|
||||||
|
- [ ] [Service 2 setup]
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
[Any follow-up TODOs or known limitations]
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **CI/CD advice for {{CICD_TOOL}} ({{CICD_RAW}}):**
|
||||||
|
Ensure workflow secrets are scoped per flavor ({{FLAVORS_LIST}}). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rules applied every phase
|
||||||
|
|
||||||
|
Always active — read before writing any code:
|
||||||
|
- `.cursor/rules/flutter-core.mdc`
|
||||||
|
- `.cursor/rules/security-standards.mdc`
|
||||||
|
- `.cursor/rules/project-context.mdc`
|
||||||
|
- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load`
|
||||||
|
|
||||||
|
Architecture import rules for **{{ARCHITECTURE}}**:
|
||||||
|
{{ARCH_IMPORT_RULES}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code generation notes
|
||||||
|
|
||||||
|
**Codegen tools configured: {{CODEGEN_LIST}}**
|
||||||
|
|
||||||
|
After adding any new model or injectable class, run:
|
||||||
|
```
|
||||||
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them.
|
||||||
|
|
||||||
|
**Template version:** {{TEMPLATE_VERSION}}
|
||||||
Reference in New Issue
Block a user