tjdrhs90/flutter-flame-harness/flame-harness-generator

Claude Code and agent skill by tjdrhs90/flutter-flame-harness

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Phase 5 — build the Flame game in 3 gated sub-phases (core loop → systems+components → UI+content), then self-evaluate against the contract.

Install

npx vskill@latest install tjdrhs90/flutter-flame-harness/flame-harness-generator

SKILL.md

flame-harness-generator

Phase 5 of the flutter-flame-harness pipeline. Builds the Flame game in three gated sub-phases (5a → 5b → 5c). Each sub-phase ends with a HARD GATE (flutter analyze zero issues + flutter test all pass) before the next sub-phase begins. On round N > 1, reads the previous evaluator feedback and fixes only the listed failures.

All file schemas (config.md, state.md, handoff/round-N-gen.md, feedback/round-N-qa.md) and the phase transition table are defined in docs/harness-protocol.md — that document is the single source of truth (§2 for state.md schema; §4 for handoff layout; §5 for feedback layout; §7 for the generator → evaluator transition). Do not redefine schemas here.


Round Handling and Feedback Intake

Determine current round

Read docs/harness/state.md. Extract current_round (integer, ≥ 1).

Round 1 — build from scratch

When current_round is 1:

  1. Read docs/harness/config.md (extract app_slug, app_name, bundle_id,

default_language, skip_admob).

  1. Read the latest PRD (docs/harness/plans/*-prd.md, sort descending, take first).
  2. Read the latest design doc (docs/harness/plans/*-design.md, sort descending, take first).
  3. Read docs/harness/contract.md. Confirm ## Status: AGREED is present; abort with

flame-harness-generator: contract not AGREED — run flame-harness-contract first if missing.

  1. Read checkpoint from state.md (see docs/harness-protocol.md §2). Re-entry: if a prior run

crashed/paused mid-phase, skip every sub-phase ≤ checkpoint (its HARD GATE already passed and its output exists) and resume at the next one — e.g. checkpoint: 5a → start at 5b. If checkpoint is "", start at 5a. This makes the generator safe to re-run without redoing (or colliding with) completed work.

Round N > 1 — fix only listed failures (feedback intake)

When current_round is greater than 1:

  1. Read docs/harness/feedback/round-<N-1>-qa.md (per docs/harness-protocol.md §5 layout).
  2. Parse the ## Failed Criteria section to extract each failing criterion and its prescribed fix.
  3. Do NOT redesign or refactor areas that were not listed as failures. Make only the minimum

changes required to satisfy the listed fixes.

  1. If docs/harness/feedback/round-<N-1>-qa.md does not exist, abort with:

flame-harness-generator: feedback file for round <N-1> not found — cannot determine fixes.

  1. Apply each fix, then run the HARD GATE for the affected sub-phase before writing the handoff.

Sub-phase 5a — Scaffold + Core Loop

5a.1 Create the Flutter project (flutter create)

Run flutter create. The Dart package name must be snake_case (lowercase + underscores, no hyphens) — convert app_slug (e.g. swing-line → swing_line). This package name is separate from the bundle id:

bash
GAME=<projects-dir>/<app_slug>
if [ -d "$GAME/lib" ]; then
echo "project already exists — reusing (skipping flutter create)"
else
flutter create --org com.<company> --project-name <app_slug_snake_case> "$GAME"
fi

Idempotency: flutter create fails if the target already exists, which would break a resume or a re-run of round 1. The guard above makes 5a.1 safe to re-enter — it reuses an existing scaffold instead of hard-failing. (If the user passed --clean, rm -rf "$GAME" first, then create fresh.)

Bundle id — set it explicitly and IDENTICALLY on both platforms (do not trust the value `flutter create` derives from the project name). flutter create builds the bundle id from --org + project-name, which can leave underscores / case differences and can diverge between iOS and Android. Force both to the canonical config.bundle_id (com.<company>.<id>, lowercase [a-z0-9] only — see docs/harness-protocol.md):

  • iOS: set PRODUCT_BUNDLE_IDENTIFIER = <bundle_id> in all three build configs

(Debug/Release/Profile) in ios/Runner.xcodeproj/project.pbxproj.

  • Android: set both applicationId and namespace = <bundle_id> in android/app/build.gradle.kts.
  • Verify iOS PRODUCT_BUNDLE_IDENTIFIER == Android applicationId == config.bundle_id,

byte-for-byte (same case, no _, no -). The AdMob app and store records use this exact id.

Important (per `docs/harness-protocol.md`): After flutter create succeeds, move the docs/harness/ directory into the game project so all artifacts share one repository:

bash
mv <projects-dir>/flutter-flame-harness/docs/harness \
<projects-dir>/<app_slug>/docs/harness

All subsequent reads/writes of harness files use the new path inside the game project.

5a.2 Remove default template files

Delete the generated counter demo to start from a clean slate:

bash
rm <projects-dir>/<app_slug>/lib/main.dart
rm <projects-dir>/<app_slug>/test/widget_test.dart

These files will be replaced by the game implementation below.

5a.3 Configure pubspec.yaml

Set the following fields in pubspec.yaml:

  • name: <app_slug> (from config.md)
  • description: <app_name> (from config.md)
  • publish_to: none
  • version: 1.0.0+1 — explicit semver MAJOR.MINOR.PATCH+BUILD (App Store marketing version =

1.0.0, not 1.0; the build phase bumps the +BUILD on each upload).

Add to dependencies (use the latest compatible versions; minimum versions shown):

yaml
dependencies:
flutter:
sdk: flutter
flame: ^1.37.0
flame_audio: ^2.0.0
google_mobile_ads: ^5.0.0
shared_preferences: ^2.0.0
flutter_secure_storage: ^9.0.0 # durable save: iOS Keychain (survives reinstall/device)
play_services_block_store: ^0.8.0 # durable save: Android Block Store (survives reinstall/device)

Add to dev_dependencies (image is used by tool/gen_icon.dart + tool/strip_bg.dart):

yaml
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter # core-loop integration test (5c.8)
flutter_lints: ^4.0.0
image: ^4.0.0
flutter_launcher_icons: ^0.14.0
flutter_native_splash: ^2.4.0

Add asset directories to flutter::

yaml
flutter:
assets:
- assets/images/
- assets/audio/
- assets/icons/

Run flutter pub get and confirm it exits 0.

5a.4 Create the lib/ directory structure

Create the following directories (and assets/ directories):

lib/
game/
components/
systems/
data/
screens/
ui/
l10n/
assets/
images/
audio/
icons/

This structure matches the PRD's lib/ layout.

5a.5 game_config.dart — all tuning constants

Create lib/game/game_config.dart. This file must centralize every tuning constant that affects gameplay (speeds, spawn rates, scores, timings, physics). No magic numbers are permitted in any other game file — all values must reference a constant in GameConfig.

Template:

dart
// lib/game/game_config.dart
// All tuning constants for <app_name>. Edit here to tweak gameplay — do not
// hard-code numbers elsewhere.
abstract class GameConfig {
// Screen / world
static const double worldWidth = 360.0;
static const double worldHeight = 640.0;
// Player
static const double playerSpeed = 200.0;
static const double playerJumpForce = 500.0;
// Scoring
static const int scorePerEnemy = 10;
static const int scorePerDistance = 1;
// Difficulty
static const double initialSpawnInterval = 2.0; // seconds
static const double minSpawnInterval = 0.4;
static const double difficultyRampRate = 0.02; // per second
// Audio
static const double bgmVolume = 0.6;
static const double sfxVolume = 0.9;
// AdMob (non-credential tuning only — IDs come from config.md)
static const int adShowIntervalSeconds = 120;
}

Fill all values from the PRD's game-mechanics section. Add more constants as needed; remove any that the specific game does not use.

5a.6 GameState enum

Create lib/game/game_state.dart:

dart
// lib/game/game_state.dart
enum GameState {
menu,
playing,
paused,
gameOver,
// Add states as the PRD requires (e.g. levelComplete, shop)
}
5a.7 FlameGame subclass

Create lib/game/<app_slug>_game.dart. This is the root FlameGame (or FlameGame with HasCollisionDetection when the PRD requires collision detection). Do not add the removed HasTappables mixin — it was dropped in Flame 1.7 and will cause a compile error. Tap input is handled via TapCallbacks on individual components (see §5a.8). It must:

  • Declare GameState _state = GameState.menu; and a getter GameState get state.
  • Expose void startGame(), void pauseGame(), void resumeGame(), void gameOver() methods

that transition _state and show/hide Flutter overlays by name.

  • Override onLoad() to load assets and add the background component (call super.onLoad() first).
  • Register all overlay names needed by screens (e.g. 'menu', 'hud', 'pause', 'gameOver').
  • Performance (see `docs/game-gotchas.md`): if components need to query peers each frame,

compute the active lists once in the game root's update(dt) and have components read that cache — never call world.children.whereType<X>() per-component per-frame. Reuse static final Paint objects; don't recreate Paint/shaders/blur every frame.

5a.8 Input handling

Wire up the input method specified in the PRD:

  • Tap/click → TapCallbacks mixin on the player component (preferred for mobile).
  • Drag → DragCallbacks mixin.
  • Keyboard → KeyboardEvents mixin on the game class.
5a.9 main.dart entry point

Create lib/main.dart:

dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flame/game.dart';
import 'game/<app_slug>_game.dart';
// import overlay screens here in 5c
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Lock to config.md `orientation` — portrait OR landscape, never both.
// (Native lock is also set in §5c.10 so there is no rotate-on-launch flash.)
await SystemChrome.setPreferredOrientations(
// portrait → [portraitUp, portraitDown]; landscape → [landscapeLeft, landscapeRight]
<DeviceOrientation>[/* fill from config.orientation */],
);
runApp(GameWidget(game: <AppSlugGame>()));
}

Replace <AppSlugGame> with the actual class name.

Startup order & lifecycle (required — see the Lifecycle patterns in `docs/game-gotchas.md`). main() must run in this order: WidgetsFlutterBinding.ensureInitialized() → await SharedPreferences.getInstance() (so synchronous prefs reads later work) → orientation → (if ads) ATT-after-first-frame → UMP consent → MobileAds.initialize() → runApp(...).

Host the GameWidget in a StatefulWidget that implements WidgetsBindingObserver: didChangeAppLifecycleState on paused/inactive → game.pauseEngine() + BGM pause; on resumed → resume. Clean up (stop audio, dispose pools, cancel timers) on the game's onDetach()/onRemove(). Gate stale input on resume with a _canInput flag.

5a.10 Minimal passing test

Create test/game_config_test.dart:

dart
import 'package:flutter_test/flutter_test.dart';
import 'package:<app_slug>/game/game_config.dart';
void main() {
test('GameConfig constants are positive', () {
expect(GameConfig.worldWidth, greaterThan(0));
expect(GameConfig.worldHeight, greaterThan(0));
expect(GameConfig.bgmVolume, inInclusiveRange(0.0, 1.0));
expect(GameConfig.sfxVolume, inInclusiveRange(0.0, 1.0));
});
}
5a.11 CI workflow

Copy templates/ci.yml.template → .github/workflows/ci.yml (GitHub Actions running flutter analyze --no-fatal-infos + flutter test on push/PR), mirroring the shipped games' CI.

5a.12 Initialize version control

The generated game is its own git repository — initialize it and commit as the build progresses, so the work has atomic history from the start (and the user can diff/revert per sub-phase).

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
60
Repository forks
5
7-day trend score
21

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/tjdrhs90/flutter-flame-harness/flame-harness-generator/badge)](https://verified-skill.com/skills/tjdrhs90/flutter-flame-harness/flame-harness-generator)

Something wrong with this skill or its page? Tell us.