novaforge-appkit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/adapters/claude-code/README.md +25 -0
  4. package/adapters/codex/AGENTS.section.md +53 -0
  5. package/adapters/codex/README.md +20 -0
  6. package/bin/appkit.js +171 -0
  7. package/install.sh +131 -0
  8. package/kit/agents/builder-agent.md +49 -0
  9. package/kit/agents/product-agent.md +48 -0
  10. package/kit/agents/release-agent.md +46 -0
  11. package/kit/agents/reviewer-agent.md +42 -0
  12. package/kit/commands/build.md +30 -0
  13. package/kit/commands/fix.md +33 -0
  14. package/kit/commands/idea.md +28 -0
  15. package/kit/commands/init.md +29 -0
  16. package/kit/commands/release.md +32 -0
  17. package/kit/commands/shape.md +33 -0
  18. package/kit/commands/verify.md +35 -0
  19. package/kit/constitution.md +79 -0
  20. package/kit/orchestrator.md +63 -0
  21. package/kit/policy-rules/admob.md +25 -0
  22. package/kit/policy-rules/apple-app-store.md +31 -0
  23. package/kit/policy-rules/google-play.md +33 -0
  24. package/kit/policy-rules/privacy.md +25 -0
  25. package/kit/profiles/android.md +31 -0
  26. package/kit/profiles/flutter.md +45 -0
  27. package/kit/profiles/ios.md +32 -0
  28. package/kit/scripts/_common.sh +24 -0
  29. package/kit/scripts/analyze.sh +6 -0
  30. package/kit/scripts/build-android.sh +12 -0
  31. package/kit/scripts/build-ios.sh +23 -0
  32. package/kit/scripts/capture-screenshots.sh +20 -0
  33. package/kit/scripts/compare-goldens.sh +21 -0
  34. package/kit/scripts/extract-dependencies.sh +23 -0
  35. package/kit/scripts/extract-permissions.sh +22 -0
  36. package/kit/scripts/format.sh +11 -0
  37. package/kit/scripts/scan-secrets.sh +15 -0
  38. package/kit/scripts/test.sh +12 -0
  39. package/kit/scripts/validate-ad-ids.sh +22 -0
  40. package/kit/scripts/validate-release.sh +30 -0
  41. package/kit/skills/admob-best-practices/SKILL.md +74 -0
  42. package/kit/skills/mobile-app-development/SKILL.md +69 -0
  43. package/kit/skills/mobile-privacy-and-permissions/SKILL.md +71 -0
  44. package/kit/skills/mobile-store-release/SKILL.md +74 -0
  45. package/kit/skills/mobile-testing-and-visual-qa/SKILL.md +79 -0
  46. package/kit/skills/small-app-product-design/SKILL.md +68 -0
  47. package/kit/templates/app-spec.template.md +89 -0
  48. package/kit/templates/idea.template.md +60 -0
  49. package/kit/templates/privacy-policy.template.md +41 -0
  50. package/kit/templates/release-manifest.template.yaml +46 -0
  51. package/kit/templates/store-listing.template.md +37 -0
  52. package/kit/templates/tasks.template.md +46 -0
  53. package/kit/templates/verification-report.template.md +58 -0
  54. package/package.json +43 -0
@@ -0,0 +1,45 @@
1
+ # Profile: Flutter (default)
2
+
3
+ The default cross-platform implementation profile. Optimized for small, local-first apps.
4
+
5
+ ## Defaults
6
+ - **Framework**: Flutter (stable channel), Dart 3.
7
+ - **State management**: start with `setState` / `ValueNotifier`. Introduce `provider` or
8
+ `riverpod` only when shared state across screens makes it worthwhile. No BLoC for small apps.
9
+ - **Local storage**: `shared_preferences` for small key/value; `sqflite` or `drift` only when
10
+ there is a real relational/list dataset. Hide it behind a repository interface.
11
+ - **Navigation**: `Navigator 2` via `go_router` if there are 3+ routes, otherwise plain
12
+ `Navigator`. Portrait-only unless content needs landscape.
13
+ - **Ads**: `google_mobile_ads`, isolated behind an `AdService`/adapter. Test ad unit ids in
14
+ debug, real ids only via `--dart-define` in release.
15
+ - **Config**: environment via `--dart-define` (`APP_ENV=dev|prod`); never hardcode prod ids.
16
+ - **DI**: constructor injection. Avoid `get_it` unless the graph is genuinely large.
17
+
18
+ ## Minimum OS targets
19
+ - **Android**: `minSdk 24` (Android 7.0), `targetSdk 35` / `compileSdk 35` (required by Google
20
+ Play for new apps since Aug 31 2025).
21
+ - **iOS**: deployment target `iOS 15.0`.
22
+
23
+ ## Suggested structure
24
+ ```
25
+ lib/
26
+ ├── app/ app.dart, routes.dart, theme.dart
27
+ ├── core/ config/, ads/, storage/, utilities/
28
+ ├── features/ home/, settings/, <core_feature>/
29
+ └── main.dart
30
+ test/ unit/, widgets/, helpers/
31
+ integration_test/ critical_flow_test.dart, screenshot_flow_test.dart
32
+ ```
33
+
34
+ ## Quality commands
35
+ - Format: `dart format .`
36
+ - Analyze: `flutter analyze`
37
+ - Test: `flutter test --coverage`
38
+ - Build Android: `flutter build appbundle --release`
39
+ - Build iOS: `flutter build ipa --release` (or `flutter build ios --release --no-codesign` on CI)
40
+ - Integration: `flutter test integration_test/`
41
+
42
+ ## Testability rules
43
+ - Keep business logic out of widgets so it is unit-testable.
44
+ - Provide deterministic seed data for screenshot/integration runs (fixed clock, fixed ids).
45
+ - Every screen state listed in `app-spec.md` must be reachable by a widget or integration test.
@@ -0,0 +1,32 @@
1
+ # Profile: iOS targeting
2
+
3
+ Applies on top of the Flutter profile for the iOS build.
4
+
5
+ ## Build configuration
6
+ - Deployment target: iOS 15.0.
7
+ - Bundle identifier: reverse-DNS, must match App Store Connect (e.g. `com.example.timer`).
8
+ - `CFBundleShortVersionString` (marketing version) and `CFBundleVersion` (build, increasing).
9
+ - Build with the current Xcode / latest iOS SDK (Apple requires builds against a recent SDK).
10
+
11
+ ## Privacy (Info.plist)
12
+ - Every accessed resource needs a clear `*UsageDescription` string explaining why
13
+ (`NSCameraUsageDescription`, `NSLocationWhenInUseUsageDescription`, etc.). Missing or vague
14
+ strings are a top rejection cause (Guideline 5.1.1).
15
+ - **Privacy manifest** (`PrivacyInfo.xcprivacy`): declare collected data types, tracking, and
16
+ reasons for required-reason APIs. Third-party SDKs (incl. Google Mobile Ads) must ship their
17
+ own privacy manifests and signatures — keep them updated.
18
+ - **App Tracking Transparency**: if any SDK tracks across apps/sites, add
19
+ `NSUserTrackingUsageDescription` and request authorization before tracking.
20
+
21
+ ## Signing & release
22
+ - Requires an Apple Developer Program account ($99/yr) — human owns this.
23
+ - Certificates + provisioning profiles via Xcode automatic signing or App Store Connect API.
24
+ - Output: `.ipa` via `flutter build ipa --release`, then upload via Xcode/Transporter.
25
+ - Use **TestFlight** for review-equivalent testing before App Store submission.
26
+
27
+ ## Store essentials
28
+ - App Privacy "nutrition label" (must match the privacy manifest and real behavior),
29
+ age rating, App Store screenshots per required device sizes, support URL, reviewer notes,
30
+ export-compliance answer (usually "no" for standard HTTPS / no custom crypto).
31
+ - AdMob: set `GADApplicationIdentifier` in `Info.plist` (real id only in release) and add
32
+ `SKAdNetworkItems` provided by AdMob for attribution.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ # Shared helpers for AppKit scripts. Source this from each script.
3
+ set -uo pipefail
4
+
5
+ APPKIT_DIR="${APPKIT_DIR:-.appkit}"
6
+ PROJECT_ROOT="${PROJECT_ROOT:-$(pwd)}"
7
+
8
+ c_red() { printf '\033[31m%s\033[0m\n' "$*"; }
9
+ c_grn() { printf '\033[32m%s\033[0m\n' "$*"; }
10
+ c_ylw() { printf '\033[33m%s\033[0m\n' "$*"; }
11
+ info() { printf '• %s\n' "$*"; }
12
+ ok() { c_grn "✓ $*"; }
13
+ warn() { c_ylw "! $*"; }
14
+ fail() { c_red "✗ $*"; }
15
+
16
+ have() { command -v "$1" >/dev/null 2>&1; }
17
+
18
+ # Skip cleanly (exit 0) when a required tool is missing, recording a gap rather than failing.
19
+ need() {
20
+ if ! have "$1"; then
21
+ warn "SKIP: '$1' not found — recording an explicit verification gap (not a pass)."
22
+ exit 0
23
+ fi
24
+ }
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ # Static analysis.
3
+ source "$(dirname "$0")/_common.sh"
4
+ need flutter
5
+ info "Running flutter analyze…"
6
+ if flutter analyze; then ok "Analysis passed"; else fail "Analysis found issues"; exit 1; fi
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+ # Build the Android release App Bundle. Production ad ids/env passed via --dart-define.
3
+ source "$(dirname "$0")/_common.sh"
4
+ need flutter
5
+ OUT="$APPKIT_DIR/release/builds/android"; mkdir -p "$OUT"
6
+ info "Building release .aab (APP_ENV=prod)…"
7
+ if flutter build appbundle --release --dart-define=APP_ENV=prod; then
8
+ AAB=$(ls -t build/app/outputs/bundle/release/*.aab 2>/dev/null | head -1)
9
+ if [ -n "${AAB:-}" ]; then cp "$AAB" "$OUT/" && ok "AAB -> $OUT/$(basename "$AAB")"; else warn "Build ok but .aab not found in default path"; fi
10
+ else
11
+ fail "Android release build failed"; exit 1
12
+ fi
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+ # Build the iOS release. Requires macOS + Xcode. Without signing, build the app for archive prep.
3
+ source "$(dirname "$0")/_common.sh"
4
+ need flutter
5
+ OUT="$APPKIT_DIR/release/builds/ios"; mkdir -p "$OUT"
6
+ if [ "$(uname)" != "Darwin" ]; then
7
+ warn "iOS builds require macOS + Xcode. Writing archive instructions instead."
8
+ cat > "$OUT/ARCHIVE_INSTRUCTIONS.md" <<'EOF'
9
+ # iOS Archive (run on macOS with Xcode)
10
+ 1. flutter build ipa --release --dart-define=APP_ENV=prod
11
+ 2. Or: open ios/Runner.xcworkspace, set Team & signing, Product > Archive.
12
+ 3. Validate the archive, then upload via Xcode Organizer / Transporter / App Store Connect API.
13
+ EOF
14
+ ok "Wrote $OUT/ARCHIVE_INSTRUCTIONS.md"; exit 0
15
+ fi
16
+ info "Building iOS release archive (.ipa)…"
17
+ if flutter build ipa --release --dart-define=APP_ENV=prod; then
18
+ IPA=$(ls -t build/ios/ipa/*.ipa 2>/dev/null | head -1)
19
+ [ -n "${IPA:-}" ] && cp "$IPA" "$OUT/" && ok "IPA -> $OUT/$(basename "$IPA")" || warn "Build ok; archive at build/ios/archive"
20
+ else
21
+ warn "Signed IPA build failed (likely signing). Try: flutter build ios --release --no-codesign"
22
+ exit 1
23
+ fi
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # Capture screenshot catalog via the integration_test driver into .appkit/screenshots/qa/.
3
+ # Requires a running emulator/simulator or device.
4
+ source "$(dirname "$0")/_common.sh"
5
+ need flutter
6
+ QA="$APPKIT_DIR/screenshots/qa"; mkdir -p "$QA"
7
+ DRIVER="integration_test/screenshot_flow_test.dart"
8
+ if [ ! -f "$DRIVER" ]; then
9
+ warn "No $DRIVER yet. Add a screenshot integration test that calls binding.takeScreenshot(id)."
10
+ exit 0
11
+ fi
12
+ if ! flutter devices 2>/dev/null | grep -qiE 'emulator|simulator|device'; then
13
+ warn "No device/emulator detected — recording a screenshot gap (not a pass)."; exit 0
14
+ fi
15
+ info "Capturing screenshots (APP_ENV=dev, seeded data)…"
16
+ if flutter test "$DRIVER" --dart-define=APP_ENV=dev --dart-define=APPKIT_SCREENSHOTS=1; then
17
+ ok "Screenshots captured to $QA (test must write PNGs there)"
18
+ else
19
+ fail "Screenshot run failed"; exit 1
20
+ fi
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+ # Compare captured QA screenshots against approved baselines (pixel-diff).
3
+ # Establish baselines by copying qa/ -> baselines/ once approved.
4
+ source "$(dirname "$0")/_common.sh"
5
+ QA="$APPKIT_DIR/screenshots/qa"; BASE="$APPKIT_DIR/screenshots/baselines"
6
+ mkdir -p "$BASE"
7
+ [ -d "$QA" ] || { warn "No QA screenshots yet."; exit 0; }
8
+ diffs=0; missing=0
9
+ for img in "$QA"/*.png; do
10
+ [ -e "$img" ] || { warn "No QA PNGs found."; exit 0; }
11
+ name=$(basename "$img"); b="$BASE/$name"
12
+ if [ ! -f "$b" ]; then warn "No baseline for $name (new state)"; missing=$((missing+1)); continue; fi
13
+ if have compare; then
14
+ d=$(compare -metric AE "$img" "$b" null: 2>&1 || true)
15
+ if [ "${d%% *}" != "0" ]; then fail "$name differs (AE=$d)"; diffs=$((diffs+1)); else ok "$name matches"; fi
16
+ else
17
+ if cmp -s "$img" "$b"; then ok "$name matches (byte)"; else fail "$name differs (byte)"; diffs=$((diffs+1)); fi
18
+ fi
19
+ done
20
+ info "Golden compare: $diffs diffs, $missing without baseline."
21
+ [ "$diffs" -eq 0 ] || exit 1
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+ # List direct dependencies and flag known SDK categories for the privacy/SDK review.
3
+ source "$(dirname "$0")/_common.sh"
4
+ [ -f pubspec.yaml ] || { warn "No pubspec.yaml found."; exit 0; }
5
+ echo "== Direct dependencies (pubspec.yaml) =="
6
+ awk '/^dependencies:/{f=1;next} /^dev_dependencies:|^[a-z]/{if(f && $0 !~ /^[[:space:]]/) f=0} f && /^[[:space:]]+[a-z]/{print " "$1}' pubspec.yaml | sed 's/://'
7
+ echo
8
+ echo "== Category flags =="
9
+ flag() { grep -qiE "(^|[^a-z])$1" pubspec.yaml && echo " [$2] $1"; }
10
+ flag google_mobile_ads ADS
11
+ flag firebase_analytics ANALYTICS
12
+ flag firebase_crashlytics CRASH
13
+ flag sentry CRASH
14
+ flag google_sign_in AUTH
15
+ flag firebase_auth AUTH
16
+ flag http NETWORK
17
+ flag dio NETWORK
18
+ flag shared_preferences STORAGE
19
+ flag sqflite STORAGE
20
+ flag drift STORAGE
21
+ echo
22
+ info "Cross-check this inventory against the privacy declarations (mobile-privacy-and-permissions)."
23
+ [ -n "$(command -v flutter)" ] && info "For the full tree run: flutter pub deps --style=compact"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Extract declared permissions from Android manifests and iOS Info.plist usage strings.
3
+ source "$(dirname "$0")/_common.sh"
4
+ echo "== Android permissions =="
5
+ found=0
6
+ while IFS= read -r -d '' m; do
7
+ echo "-- $m"
8
+ grep -oE 'android:name="[^"]+"' "$m" | grep -i permission | sed 's/android:name=//; s/"//g' | sort -u
9
+ found=1
10
+ done < <(find android -name AndroidManifest.xml -print0 2>/dev/null)
11
+ [ "$found" = 1 ] || warn "No AndroidManifest.xml found."
12
+ echo
13
+ echo "== iOS usage-description keys (Info.plist) =="
14
+ PLIST="ios/Runner/Info.plist"
15
+ if [ -f "$PLIST" ]; then
16
+ grep -oE 'NS[A-Za-z]+UsageDescription' "$PLIST" | sort -u || warn "No usage-description keys."
17
+ else
18
+ warn "No ios/Runner/Info.plist found."
19
+ fi
20
+ echo
21
+ info "Map each permission to feature → user benefit → runtime text → store declaration in app-spec.md."
22
+ info "Unjustified or high-risk permissions are release blockers (see policy-rules/privacy.md)."
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+ # Format Dart/Flutter sources. CI mode (--check) fails if formatting is needed.
3
+ source "$(dirname "$0")/_common.sh"
4
+ need dart
5
+ if [ "${1:-}" = "--check" ]; then
6
+ info "Checking formatting (dart format --set-exit-if-changed)…"
7
+ dart format --output=none --set-exit-if-changed . && ok "Formatting clean" || { fail "Needs formatting"; exit 1; }
8
+ else
9
+ info "Formatting (dart format .)…"
10
+ dart format . && ok "Formatted"
11
+ fi
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ # Heuristic secret scan over source + config. Flags likely keys; not a substitute for a vault.
3
+ source "$(dirname "$0")/_common.sh"
4
+ PATTERNS='(AIza[0-9A-Za-z_-]{30,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|sk_live_[0-9A-Za-z]+|ghp_[0-9A-Za-z]{30,}|aws_secret_access_key|password\s*=\s*["'\''][^"'\'' ]{6,})'
5
+ SCAN_DIRS="lib android/app ios/Runner"
6
+ hits=0
7
+ for d in $SCAN_DIRS; do
8
+ [ -e "$d" ] || continue
9
+ while IFS= read -r line; do echo "$line"; hits=$((hits+1)); done < <(grep -rInE "$PATTERNS" "$d" \
10
+ --include='*.dart' --include='*.gradle' --include='*.kts' --include='*.plist' \
11
+ --include='*.properties' --include='*.xml' --include='*.json' 2>/dev/null)
12
+ done
13
+ # Keystores / key.properties must never be committed.
14
+ find android -name '*.jks' -o -name '*.keystore' 2>/dev/null | while read -r k; do fail "Keystore in tree: $k"; hits=1; done
15
+ if [ "$hits" -eq 0 ]; then ok "No obvious secrets found"; else fail "$hits potential secret(s) — review above"; exit 1; fi
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+ # Run tests with coverage. Pass a path to scope (e.g. test/unit) for targeted runs.
3
+ source "$(dirname "$0")/_common.sh"
4
+ need flutter
5
+ TARGET="${1:-}"
6
+ info "Running flutter test ${TARGET:-(all)} --coverage…"
7
+ if flutter test --coverage $TARGET; then
8
+ ok "Tests passed"
9
+ [ -f coverage/lcov.info ] && info "Coverage: coverage/lcov.info"
10
+ else
11
+ fail "Tests failed"; exit 1
12
+ fi
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Ensure test ad ids aren't shipped in release and prod ids aren't hardcoded in source.
3
+ # Google sample/test ad unit ids contain 3940256099942544.
4
+ source "$(dirname "$0")/_common.sh"
5
+ TEST_ID="3940256099942544"
6
+ issues=0
7
+ # Test ids must not appear outside test/dev-guarded code in a way that ships to release.
8
+ hits=$(grep -rInE "ca-app-pub-${TEST_ID}" lib 2>/dev/null || true)
9
+ if [ -n "$hits" ]; then
10
+ warn "Test ad unit id referenced in lib/ — ensure it's only used when !AppConfig.isProduction:"
11
+ echo "$hits"
12
+ fi
13
+ # Real ad ids should come from env/config, not be hardcoded as production constants.
14
+ prod=$(grep -rInE 'ca-app-pub-[0-9]{16}~[0-9]+|ca-app-pub-[0-9]{16}/[0-9]+' lib 2>/dev/null | grep -v "$TEST_ID" || true)
15
+ if [ -n "$prod" ]; then
16
+ fail "Hardcoded production-looking ad id in lib/ — source from environment configuration:"
17
+ echo "$prod"; issues=$((issues+1))
18
+ fi
19
+ # AdMob App ID present in platform config?
20
+ grep -rq "com.google.android.gms.ads.APPLICATION_ID" android 2>/dev/null && ok "Android AdMob App ID meta-data present" || warn "Android AdMob App ID meta-data not found (ok if no ads)"
21
+ grep -rq "GADApplicationIdentifier" ios 2>/dev/null && ok "iOS GADApplicationIdentifier present" || warn "iOS GADApplicationIdentifier not found (ok if no ads)"
22
+ [ "$issues" -eq 0 ] && ok "Ad id check passed" || { fail "Ad id issues found"; exit 1; }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Static release-readiness checks: versions, ids, target API, env, debug leftovers.
3
+ source "$(dirname "$0")/_common.sh"
4
+ issues=0
5
+ note_fail(){ fail "$1"; issues=$((issues+1)); }
6
+
7
+ # Version present in pubspec
8
+ if [ -f pubspec.yaml ]; then
9
+ v=$(grep -E '^version:' pubspec.yaml | awk '{print $2}')
10
+ [ -n "$v" ] && ok "pubspec version: $v" || note_fail "No version: in pubspec.yaml"
11
+ else note_fail "No pubspec.yaml"; fi
12
+
13
+ # Android target/compile SDK 35
14
+ GR="android/app/build.gradle"; GRK="android/app/build.gradle.kts"
15
+ gf=""; [ -f "$GR" ] && gf="$GR"; [ -f "$GRK" ] && gf="$GRK"
16
+ if [ -n "$gf" ]; then
17
+ ts=$(grep -oE 'targetSdk(Version)?[ =]+[0-9]+' "$gf" | grep -oE '[0-9]+' | head -1)
18
+ if [ -n "$ts" ] && [ "$ts" -ge 35 ]; then ok "Android targetSdk=$ts (>=35)"; else note_fail "Android targetSdk=$ts (<35; Play requires 35)"; fi
19
+ else warn "No android build.gradle found."; fi
20
+
21
+ # Debug / placeholder leftovers in lib
22
+ if grep -rInE '\b(TODO|FIXME|Lorem ipsum|placeholder)\b' lib >/dev/null 2>&1; then warn "Placeholder/TODO text in lib/ — review before release."; fi
23
+ if grep -rInE '\bprint\(' lib >/dev/null 2>&1; then warn "print() calls in lib/ — prefer no debug logging in release."; fi
24
+
25
+ # Run the ad-id and secret checks
26
+ bash "$(dirname "$0")/validate-ad-ids.sh" || issues=$((issues+1))
27
+ bash "$(dirname "$0")/scan-secrets.sh" || issues=$((issues+1))
28
+
29
+ echo
30
+ if [ "$issues" -eq 0 ]; then ok "Static release validation passed (still run a real release smoke test on device)."; else fail "$issues release issue(s) — see above."; exit 1; fi
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: admob-best-practices
3
+ description: Plan and integrate safe, policy-compliant AdMob monetization. Use during Shape, Build and Verify for ad-format selection, banner/interstitial/rewarded/app-open placement rules, frequency and natural-transition rules, accidental-click prevention, test vs production ad ids, consent (UMP/GDPR), child-directed settings, failure/offline behavior and the AdMob release checklist.
4
+ ---
5
+
6
+ # AdMob Best Practices
7
+
8
+ Monetize without harming usability or violating policy. Ads are part of UX, decided in Shape.
9
+
10
+ ## Format selection (default preference order)
11
+ 1. **Anchored adaptive banner** — safe default, low risk, persistent.
12
+ 2. **Rewarded** — only user-initiated, with a clear value exchange.
13
+ 3. **Interstitial** — only at natural transitions (level end, save, screen completion).
14
+ 4. **App-open** — only on cold start / return to foreground, infrequently.
15
+ 5. **Native** — only when the app naturally has a feed; needs careful labeling.
16
+
17
+ Avoid advanced formats unless the app naturally supports them. Many small utilities should
18
+ ship **banner-only** or no ads.
19
+
20
+ ## Placement spec (every placement, in app-spec.md)
21
+ ```yaml
22
+ placement_id: home_bottom_banner
23
+ format: anchored_adaptive_banner
24
+ screen: HOME
25
+ trigger: screen_visible
26
+ frequency: sdk_managed
27
+ critical_interaction_conflict: false
28
+ accidental_click_risk: low
29
+ loading_behavior: preserve_layout
30
+ failure_behavior: collapse_or_preserve_clean_space
31
+ development_ad_id: test
32
+ production_ad_id_source: environment_configuration
33
+ ```
34
+
35
+ ## Hard policy rules (AdMob / Google Play / Apple)
36
+ - **Never** place ads where they obstruct content or sit next to interactive controls
37
+ (accidental clicks = policy violation and account risk).
38
+ - **No** ads that simulate app UI, "X" buttons that lead to clicks, or unexpected fullscreen
39
+ ads. Interstitials must not appear on app launch or mid-action.
40
+ - **Rewarded** ads must be explicitly user-initiated and clearly described before play.
41
+ - **App-open** ads must not be shown aggressively or block the user repeatedly.
42
+ - Do not click your own ads or encourage clicks. Do not place ads on screens with no content.
43
+ - Maintain reasonable ad density; one banner per screen is plenty for small apps.
44
+
45
+ ## Test vs production ids
46
+ - Use Google's **sample/test ad unit ids** (and register test devices) during development.
47
+ - Real ad unit ids only in release via environment config. Hardcoded production ids in debug
48
+ builds or test ids in release are **critical** issues.
49
+ - The AdMob **App ID** goes in `AndroidManifest.xml` and `Info.plist`; use the real id only in
50
+ release.
51
+
52
+ ## Consent & privacy
53
+ - Implement Google **UMP / User Messaging Platform** for GDPR/EEA + UK consent and the CCPA
54
+ signal where required. Gate personalized ads on consent; fall back to non-personalized ads.
55
+ - Reflect the advertising id / tracking in Data Safety, App Privacy, and the privacy manifest
56
+ (see `mobile-privacy-and-permissions`).
57
+ - iOS: personalized ads that track require ATT authorization. Non-personalized ads avoid this.
58
+
59
+ ## Child-directed
60
+ If the app targets children, set `tagForChildDirectedTreatment` / `tagForUnderAgeOfConsent`,
61
+ serve only family-safe, non-personalized ads (or no ads), and confirm with the human first.
62
+
63
+ ## Failure & offline behavior
64
+ - Ad load failure must degrade cleanly: collapse the slot or keep clean reserved space; never
65
+ block content or show error UI. The app must be fully usable offline with no ads.
66
+
67
+ ## Verify checklist
68
+ - [ ] Test ids in dev, real ids only in release (scripted check).
69
+ - [ ] App ID set per platform, env-gated.
70
+ - [ ] Each placement matches `app-spec.md`; no obstruction / no risky adjacency.
71
+ - [ ] Interstitials only at natural transitions; rewarded user-initiated; app-open not aggressive.
72
+ - [ ] Failure & offline paths preserve usability.
73
+ - [ ] Consent (UMP) wired; child-directed settings correct where relevant.
74
+ - [ ] No hardcoded secrets.
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: mobile-app-development
3
+ description: Implement small local-first Flutter mobile apps. Use during the Build and Fix phases for project setup, feature-oriented structure, state management, navigation, local storage, environment config, the AdMob adapter, dependency hygiene, release configuration and writing testable code.
4
+ ---
5
+
6
+ # Mobile App Development
7
+
8
+ Implementation guidance for the default Flutter profile. Keep architecture **proportional**
9
+ to a small app — the goal is a shippable MVP, not a framework.
10
+
11
+ ## Project setup
12
+ - `flutter create --org com.example --platforms=android,ios <name>`.
13
+ - Set application id / bundle id immediately and consistently (they are hard to change later).
14
+ - Pin the Flutter/Dart SDK constraint in `pubspec.yaml`. Commit `pubspec.lock`.
15
+ - Configure `analysis_options.yaml` with `flutter_lints`.
16
+
17
+ ## Structure (feature-oriented)
18
+ ```
19
+ lib/
20
+ ├── app/ app.dart (MaterialApp), routes.dart, theme.dart
21
+ ├── core/ config/ (env), ads/ (AdService), storage/ (repository), utilities/
22
+ ├── features/ <feature>/ -> screen widgets + a controller/notifier + models
23
+ └── main.dart
24
+ ```
25
+ - Separate **UI / state / storage**. Widgets render; controllers hold logic; repositories
26
+ persist. This keeps logic unit-testable and storage swappable.
27
+
28
+ ## State management
29
+ - Default to `setState`/`ValueNotifier`/`ChangeNotifier`. Reach for `provider`/`riverpod`
30
+ only when state is shared across screens. No global mutable state unless justified.
31
+
32
+ ## Local storage
33
+ - Hide persistence behind a `Repository` interface; provide an in-memory fake for tests.
34
+ - `shared_preferences` for small key/value; `drift`/`sqflite` for lists/relations.
35
+ - Support a trivial **migration** hook (a stored schema version) even in v1.
36
+
37
+ ## Navigation & UX
38
+ - Portrait by default. Use `go_router` at 3+ routes.
39
+ - Handle empty / loading / error states for every screen — they are spec'd and screenshotted.
40
+ - Keyboard-aware: wrap forms in scroll views and respect `MediaQuery.viewInsets` so primary
41
+ actions never hide behind the keyboard.
42
+
43
+ ## Environment configuration
44
+ - `APP_ENV` via `--dart-define`. A `core/config/AppConfig` exposes `isProduction`, ad ids, etc.
45
+ - **Never** hardcode production ad ids or endpoints. Production values come from
46
+ `--dart-define` at release-build time only.
47
+
48
+ ## AdMob adapter
49
+ - One `AdService` wraps `google_mobile_ads`. The rest of the app talks only to `AdService`.
50
+ - It returns **test** ad unit ids unless `AppConfig.isProduction`. Loading failures must
51
+ return cleanly so the UI degrades gracefully (collapse/preserve space, never block content).
52
+ - See `admob-best-practices` for placement rules.
53
+
54
+ ## Dependencies
55
+ - Each new dependency must earn its place. Prefer the platform/std lib. Audit every package's
56
+ permissions, SDKs, and privacy impact before adding it (see `mobile-privacy-and-permissions`).
57
+
58
+ ## Release configuration hygiene
59
+ - No debug menus, `print`/log spam, placeholder text, or test ad ids in release builds.
60
+ - Correct application id, version, and `APP_ENV=prod`. Enable code shrinking on Android.
61
+
62
+ ## Change control
63
+ Do not silently change product objective, target user, monetization, data collection,
64
+ permissions, backend usage, store target, or acceptance criteria. If implementation forces a
65
+ change, update `app-spec.md` and note the reason (constitution §9).
66
+
67
+ ## Testability
68
+ Write tests alongside code (see `mobile-testing-and-visual-qa`). If something is hard to test,
69
+ it is usually badly factored — move logic out of the widget.
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: mobile-privacy-and-permissions
3
+ description: Ensure minimal data handling and accurate privacy declarations for mobile apps. Use across all phases for data and SDK inventory, personal/sensitive data classification, data minimization, permission justification and runtime messaging, privacy-policy drafting, and Google Data Safety + Apple App Privacy + iOS privacy manifest mapping from real implementation evidence.
4
+ ---
5
+
6
+ # Mobile Privacy & Permissions
7
+
8
+ Privacy is a release gate. Declarations must match what the code actually does.
9
+
10
+ ## Data inventory (build from evidence, not intent)
11
+ For each piece of data the app touches, record:
12
+ `name · type · source · stored where (local/remote) · transmitted? · purpose · retention ·
13
+ deletion · linked to user identity? · used for tracking?`
14
+
15
+ Local-first apps should be able to answer: "all data stays on device, nothing transmitted".
16
+ If a third-party SDK breaks that, it must appear in the inventory.
17
+
18
+ ## SDK inventory
19
+ List direct + relevant transitive dependencies and classify each: advertising, analytics,
20
+ crash-reporting, auth, storage, network. For each, note what data it collects and whether it
21
+ ships a privacy manifest (iOS). The classic small-app footprint is just **Google Mobile Ads**.
22
+
23
+ ## Data classification
24
+ - **Personal**: name, email, precise location, device identifiers (incl. advertising id),
25
+ contacts, photos.
26
+ - **Sensitive**: health, finance, sexual orientation, religion, precise location, children's
27
+ data, biometrics. Any sensitive data triggers human confirmation (constitution §8).
28
+
29
+ ## Minimization
30
+ Collect nothing you do not use. Prefer on-device processing. No analytics unless justified and
31
+ declared. No advertising id if ads can run non-personalized.
32
+
33
+ ## Permissions — justification map
34
+ Each requested permission must map to: requirement → feature → user benefit → runtime
35
+ explanation → store declaration. **An unjustified permission is a release blocker.**
36
+
37
+ High-risk permissions (block unless human-confirmed): fine/background location, contacts, SMS,
38
+ call log, accessibility service, VPN, device admin, all-files/external-storage,
39
+ `QUERY_ALL_PACKAGES`, `REQUEST_INSTALL_PACKAGES`, camera/mic when not core.
40
+
41
+ ### Runtime messaging
42
+ - Android: request at point of use with rationale; respect denial.
43
+ - iOS: every accessed resource needs a clear `*UsageDescription` in `Info.plist`. Vague strings
44
+ are a top rejection cause (Guideline 5.1.1).
45
+
46
+ ## Google Play Data Safety mapping
47
+ Fill the Data Safety form from the data inventory: data types collected/shared, whether linked
48
+ to identity, whether used for tracking, encryption in transit, deletion request mechanism.
49
+ Must match the in-app behavior and the privacy policy. Note the **advertising id** handling
50
+ and declare the `AD_ID` permission consistently.
51
+
52
+ ## Apple App Privacy + privacy manifest mapping
53
+ - App Privacy "nutrition label" in App Store Connect: data types, linkage, tracking — must
54
+ match real behavior and the privacy manifest.
55
+ - `PrivacyInfo.xcprivacy`: declare collected data types, tracking flag, and **required-reason
56
+ API** usage. Bundled SDKs (incl. Google Mobile Ads) must provide their own manifests +
57
+ signatures; keep them current or the build is rejected.
58
+ - **App Tracking Transparency**: if any SDK tracks across apps/sites, request authorization via
59
+ ATT and add `NSUserTrackingUsageDescription`. If you only show non-personalized ads, you can
60
+ avoid tracking and say so consistently.
61
+
62
+ ## Privacy policy
63
+ Even a local-only app usually needs a hosted privacy policy URL (both stores require one when
64
+ ads/SDKs are present). Draft from the data + SDK inventory. Use the template in
65
+ `templates/privacy-policy.template.md`. Mark every placeholder (`<HOSTED_URL>`) clearly; never
66
+ invent legal commitments — flag them for human review.
67
+
68
+ ## Children
69
+ If the audience includes children: Google Play Families policy and Apple Kids Category apply,
70
+ ads must use child-directed/`tagForChildDirectedTreatment` settings, no behavioral tracking,
71
+ and COPPA/GDPR-K obligations attach. Always escalate to the human first.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: mobile-store-release
3
+ description: Guide store review, signing, metadata, screenshots, declarations, release preparation and rejection handling for Google Play and the Apple App Store. Use during Verify and Release for policy review, Android/iOS signing, versioning, store metadata, content rating, reviewer notes, test tracks/TestFlight, release checklists and store-rejection classification.
4
+ ---
5
+
6
+ # Mobile Store Release
7
+
8
+ Prepare complete, policy-aware release packages. The kit prepares; the human submits.
9
+
10
+ ## Google Play review (inspect)
11
+ - **Functionality / minimum-functionality**: app must do something useful and complete — no
12
+ placeholder/"coming soon", no webview-only wrappers.
13
+ - **Target API**: new apps & updates must target **API 35 (Android 15)** since Aug 31 2025.
14
+ - **Permissions**: every permission justified; sensitive permissions need declarations.
15
+ - **Data Safety** form consistent with real behavior + privacy policy; declare advertising id.
16
+ - **Ads** declaration set; ad behavior compliant (see `admob-best-practices`).
17
+ - **Content rating** via IARC questionnaire; **target audience & content** (Families if kids).
18
+ - **Privacy policy URL** required (always when ads/SDKs present).
19
+ - **Account/app access** info if there is a login or gated content.
20
+ - **App signing**: enroll in Play App Signing; upload `.aab`.
21
+ - **Testing track**: new personal developer accounts must run closed testing before production.
22
+ - Correct, immutable **package name**; correct version code/name.
23
+
24
+ ## Apple App Store review (inspect)
25
+ - **Guideline 2.1 App Completeness** (top rejection ~40%): no crashes, no bugs, no placeholder
26
+ content, all features functional, demo/login provided if needed.
27
+ - **Guideline 5.1.1 Privacy**: clear permission purpose strings; App Privacy label matches
28
+ behavior; privacy manifest present; ATT if tracking.
29
+ - **Stability**: must not crash on the reviewer's device / current iOS.
30
+ - **Business model** clear; ads behave; no hidden/undocumented features.
31
+ - **Metadata** accurate (no misleading screenshots/claims); **reviewer notes** explain
32
+ non-obvious behavior; demo account if applicable.
33
+ - **Export compliance** answer (usually "no" for standard HTTPS).
34
+ - Correct **bundle id**; signing & provisioning ready; build against current SDK.
35
+ - Use **TestFlight** before submission.
36
+
37
+ ## Signing (never expose secrets)
38
+ **Android**: create an upload keystore (`keytool`), store it + passwords outside the repo
39
+ (secrets manager / CI secret); configure `signingConfigs` to read from env/`key.properties`
40
+ (git-ignored); enroll in Play App Signing; verify with `apksigner`/`bundletool`.
41
+ **iOS**: own bundle id in App Store Connect; team + certificates + provisioning profile (Xcode
42
+ automatic or ASC API key); archive and validate before upload. The **human owns** signing
43
+ identities and the developer accounts.
44
+
45
+ ## Versioning
46
+ - Android: `versionName` (semver, e.g. `1.0.0`) + integer `versionCode` (always increasing).
47
+ - iOS: `CFBundleShortVersionString` + increasing `CFBundleVersion`. Keep them in sync with
48
+ `pubspec.yaml` `version: 1.0.0+1`.
49
+
50
+ ## Store metadata
51
+ App name · short/full description · subtitle (iOS) · keywords (iOS) · category · promotional
52
+ text · release notes · privacy-policy/support/marketing URLs (placeholders OK, marked) ·
53
+ reviewer notes. No misleading claims; do not show unavailable features.
54
+
55
+ ## Store screenshots
56
+ Separate QA screenshots from marketing screenshots:
57
+ `screenshots/qa/` · `screenshots/store-raw/` · `screenshots/store-final/`.
58
+ Marketing shots reflect the real release build, use realistic safe sample data, match required
59
+ platform dimensions, avoid debug data and misleading content.
60
+
61
+ ## Release checklist (per platform)
62
+ - [ ] Verification READY (or approved with warnings), no critical/unresolved-major issues.
63
+ - [ ] Correct app id / bundle id, version, `APP_ENV=prod`, real ad ids only here.
64
+ - [ ] Signing configured, secrets out of repo.
65
+ - [ ] Metadata, screenshots, privacy declarations, reviewer notes prepared.
66
+ - [ ] Privacy policy + support URLs resolved.
67
+ - [ ] Human approval recorded before submission.
68
+
69
+ ## Store-rejection classification (for `/appkit.fix rejection`)
70
+ Map the rejection text to a category → requirement → code/metadata fix:
71
+ minimum functionality · crash/bug · privacy/permission · metadata/screenshot · ads ·
72
+ data-safety/privacy-label mismatch · account access · IP/content · signing/build config.
73
+ Prefer the **minimal** change, add a regression test/check, update the affected declaration,
74
+ then re-verify before resubmitting. Document the change for the resubmission notes.