Phase 4 — propose verifiable completion criteria and mandatory hard gates; reach AGREED (1-pass default, multi-round negotiation in --strict).
Scanned 8/31/2026
Install to Claude Code
npx -y skills add tjdrhs90/flutter-flame-harness --skill flame-harness-contract --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Flame Harness Contract?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/tjdrhs90-flame-harness-contract)More formats (shields.io, HTML) on the badges page.
---
name: flame-harness-contract
description: Phase 4 — propose verifiable completion criteria and mandatory hard gates; reach AGREED (1-pass default, multi-round negotiation in --strict).
argument-hint: ""
allowed-tools: [Agent, Read, Write, Edit, Bash]
---
# flame-harness-contract
Phase 4 of the flutter-flame-harness pipeline. Reads the PRD and design doc, proposes verifiable
completion criteria (Mandatory Hard Gates + Functional Criteria), and marks the contract AGREED
before handing off to the generator.
All file schemas (`config.md`, `state.md`, `contract.md`) are defined in
`docs/harness-protocol.md` — refer to that document as the single source of truth. Do not
redefine schemas here.
---
## Input
### 1. Read `docs/harness/config.md`
Extract:
| Key | Use |
|---|---|
| `app_name` | Contract title header |
| `app_slug` | Used to identify the game |
| `strict_mode` | If `true`, run `--strict` negotiation; if `false` (or absent), run default 1-pass |
### 2. Read the latest PRD
Find the most recent file matching `docs/harness/plans/*-prd.md` (sort by filename descending,
take the first). Abort with a clear message if none exists.
### 3. Read the design doc
Find the most recent file matching `docs/harness/plans/*-design.md` (sort by filename
descending, take the first). Abort if none exists.
---
## Mandatory Hard Gates
These 8 criteria are non-negotiable and must appear verbatim in every `contract.md`. A FAIL on
any one results in an immediate FAIL verdict, regardless of other passing criteria.
> **Source:** `docs/harness-protocol.md` §3 — Mandatory Hard Gates block. Always cite; never
> restate differently.
1. `flutter analyze` returns zero issues.
2. `flutter test` — all tests pass.
3. No TODO, stub, or placeholder in game logic (grep-checkable).
4. All tuning constants centralized in `game_config.dart` — no magic numbers in gameplay code.
5. Game content (enemies / levels / waves) is defined as data, not hardcoded.
6. Localization complete for all configured locales — `default_language`, plus English when `default_language` ≠ `en` — no missing l10n keys.
7. Core loop works end to end: start → play → win/lose → restart.
8. Runs on a simulator/emulator with zero crashes and zero console errors.
Copy these 8 lines verbatim into the `## Mandatory Hard Gates` section of `contract.md`. Do not
paraphrase, reorder, or omit any gate.
## Platform-Robustness Gates
In addition to the 8 core gates above, every contract includes a `## Platform-Robustness Gates`
section requiring the patterns in `docs/game-gotchas.md` (cite it). These are mandatory:
- **R1 Audio safe**: all audio calls in try/catch (missing asset never crashes); frequent SFX use an
`AudioPool` (not per-call `FlameAudio.play()`); BGM stops on game-over, app-background, and teardown.
- **R2 Haptics safe** (if the game uses haptics): a `Haptics` helper with platform guard + throttle +
`enabled` toggle + try/catch; gameplay never calls `HapticFeedback.*` raw.
- **R3 Lifecycle**: app host implements `WidgetsBindingObserver`; on background → `pauseEngine()` +
BGM pause; resume reverses; teardown cleans up audio/timers.
- **R4 Performance**: no per-frame `world.children.whereType<...>()` in a hot path (cache once per
frame); `Paint`/shaders not recreated per frame.
- **R5 App branding**: a custom icon + splash are generated (not the default Flutter art), the icon
is opaque (no alpha), and the localized app display name (`CFBundleDisplayName`/`android:label`) is
set to `app_name` — not "Runner"/the slug.
- **R6 Native config**: orientation locked **natively** to `config.orientation` (the unused
orientation removed — no launch rotate); iPhone-only (`TARGETED_DEVICE_FAMILY = 1`, no iPad);
`ITSAppUsesNonExemptEncryption = false`; root back-button = SnackBar double-press-to-exit;
bundle id **byte-identical** across iOS (`PRODUCT_BUNDLE_IDENTIFIER`) and Android (`applicationId`)
== `config.bundle_id` (lowercase, no `_`/`-`).
- **R7 Assets & CI**: the game ships with audio (synthesized or sourced) + visuals (code-drawn or
cleaned sprites) and **no missing-asset references** (every `pubspec.yaml` asset exists); a
`.github/workflows` CI (analyze + test) is present.
- **R8 Store graphics**: Android Play listing has the required **hi-res icon (512×512)** and
**feature graphic (1024×500)** placed for `supply` (`metadata/android/<locale>/images/icon.png` +
`featureGraphic.png`); iOS has localized screenshots.
- **R9 Durable save**: persistence survives reinstall **and** device transfer (not `shared_preferences`
alone) — a `SaveRepository` mirrors the save blob to iOS Keychain (`flutter_secure_storage`,
`first_unlock`) + Android Block Store (`play_services_block_store`) + a `shared_preferences` cache,
reading durable-first and writing all tiers in try/catch; `PreferencesService` routes through it.
- **R10 Accessibility & safety baseline**: no effect flashes faster than 3×/second (photosensitive
safety); OS Reduce Motion (`MediaQuery.disableAnimations`) is read and damps screen-shake/particles/
flashing; menu/overlay buttons are ≥48×48 dp with `Semantics` labels (icon-only buttons especially);
in-game HUD keeps `withNoTextScaling`. (Full in-game screen-reader support is out of scope.)
- **R11 Test depth**: beyond `flutter test` merely passing, the game ships **≥3 system unit tests**
(score/spawn/difficulty/economy — real logic), **≥1 widget test** (a menu/overlay renders + a button
taps), and **≥1 integration test** (core loop: boot → play → game-over). Mirrors the shipped games;
prevents a green `flutter test` that actually covers nothing.
---
## Functional Criteria
Per-game acceptance criteria derived from the PRD. Each criterion must be verifiable by one of:
- **Command** — a shell command whose exit code or output proves the criterion (e.g.,
`grep -r "TODO" lib/` returns no matches).
- **Screenshot** — a labelled screenshot that visually confirms the criterion.
- **Code path** — a named file + method that implements the behaviour, plus a test that exercises it.
### Writing Functional Criteria
Read the PRD sections carefully:
| PRD section | What to extract |
|---|---|
| Core Loop | One criterion per loop step (each step must be observable) |
| Game Mechanics | One criterion per mechanic; specify measurable threshold |
| Win/Lose Conditions | Explicit pass/fail states |
| Content Metrics | Count targets (e.g. "≥ 3 enemy types defined in data files") |
| Progression & Economy | Score increments, reward triggers |
Criteria that are NOT verifiable (e.g. "the game is fun") must be rewritten as observable checks
or removed. Every criterion that references a timing threshold must cite a concrete number (e.g.
"responds within 100 ms" not "responds quickly").
Aim for 5–10 Functional Criteria per game. More is not better — specificity is.
### Anti-stub rule
Before marking AGREED, run:
```bash
grep -rn "TODO\|stub\|placeholder\|스텁\|미구현" lib/ --include="*.dart"
```
If this command returns any output, the contract must NOT be marked AGREED. Remove or resolve
every stub before proceeding.
---
## Negotiation
### Default mode (1-pass)
When `strict_mode` is `false` or absent:
1. Generator reads the PRD and design doc.
2. Generator writes `docs/harness/contract.md` with:
- The 8 Mandatory Hard Gates verbatim (from `docs/harness-protocol.md` §3).
- 5–10 Functional Criteria derived from the PRD.
3. Generator self-reviews each criterion for verifiability.
4. Generator marks the contract `## Status: AGREED` in the same pass.
5. Generator updates `state.md` and hands off to the generator phase.
### --strict mode (rigorous self-review)
When `strict_mode: true` in `config.md`, or when the skill is invoked with `--strict`:
1. Generator writes the initial `contract.md` (same as default, steps 1–3 above).
2. Generator performs a **second self-review pass** over every Functional Criterion, checking
each one against all three of the following gates:
- **Concrete verification method** — does the criterion name a specific shell command (with
expected exit code or output), a labelled screenshot, or a named file + method + test?
Vague language like "works correctly" or "looks good" must be rewritten with a measurable
check.
- **Concrete numbers** — every timing, count, or threshold must cite a real value (e.g.
"responds within 100 ms", "≥ 3 enemy types", "score increments by 10 per obstacle").
Relative terms ("quickly", "several", "enough") must be replaced.
- **Full PRD coverage** — confirm that every core loop step, mechanic, win/lose condition,
and content metric from the PRD has at least one corresponding Functional Criterion.
3. If any criterion fails the second-pass check, revise it in-place before proceeding.
4. Generator self-marks `## Status: AGREED` and proceeds (same `state.md` write as default mode).
> **Phase B note:** Phase B may reintroduce Evaluator-side contract negotiation with a
> Generator↔Evaluator round-trip; Phase A uses rigorous self-review only.
In both modes the contract file must contain `## Status: AGREED` before the generator phase
begins coding.
---
## Output
### 1. Write `docs/harness/contract.md`
Use the layout defined in `docs/harness-protocol.md` §3:
```markdown
# Contract — <app_name>
## Mandatory Hard Gates
These criteria are non-negotiable. A FAIL on any one of these results in an immediate FAIL verdict,
regardless of other passing criteria.
1. `flutter analyze` returns zero issues.
2. `flutter test` — all tests pass.
3. No TODO, stub, or placeholder in game logic (grep-checkable).
4. All tuning constants centralized in `game_config.dart` — no magic numbers in gameplay code.
5. Game content (enemies / levels / waves) is defined as data, not hardcoded.
6. Localization complete for all configured locales — `default_language`, plus English when `default_language` ≠ `en` — no missing l10n keys.
7. Core loop works end to end: start → play → win/lose → restart.
8. Runs on a simulator/emulator with zero crashes and zero console errors.
## Functional Criteria (per game)
<!-- Generator adds game-specific acceptance criteria here. -->
<!-- Each criterion must be verifiable by command, screenshot, or code path. -->
## Status: AGREED
```
If `docs/harness/` does not exist, create it before writing.
### 2. Update `state.md`
Update `docs/harness/state.md` per the schema in `docs/harness-protocol.md` §2:
```yaml
status: running
current_phase: contract
next_role: generator
current_round: 1
updated_at: "<ISO-8601 UTC now>"
```
Leave all other keys unchanged. Use `Edit` for a targeted update.
Per `docs/harness-protocol.md` §7 (Phase Transition Table): the `contract → AGREED` event sets
`next_role: generator` and `current_round: 1`.
### 3. Append to `pipeline-log.md`
Append one row to `docs/harness/pipeline-log.md` per the schema in `docs/harness-protocol.md` §6:
```
| <ISO-8601 UTC now> | complete | contract | contract AGREED; <N> functional criteria; next: generator |
```
---
## Error handling
- If the PRD is missing, abort with a clear message and set `state.md` to `status: paused`,
`pause_reason: manual_action`.
- If the design doc is missing, abort with a clear message and set `state.md` to
`status: paused`, `pause_reason: manual_action`.
- If `config.md` cannot be read, abort immediately (do not write partial output).
- In `--strict` mode, the Generator self-marks AGREED after the rigorous second-pass review;
there is no multi-round Evaluator loop in Phase A. `max_rounds` applies only to the
generator→evaluator cycle (Phase 5–6), not to contract negotiation.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!