Skip to content
Back to skills

Yume Screen Flow Designer

ASecurity

Design screens.json: which shell screens exist (title, pause, settings, save slots, game over), their layout, navigation, on_click effect chains, modal vs replace, freeze_world. Use when a game needs or changes its UI shell.

  • 25 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
developmentgoshellnode

Works with

  • cli

Security analysis

A100/100

Scanned October 18, 2026

npx -y skills add kamwoh/yume --skill yume-screen-flow-designer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Yume Screen Flow Designer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Yume Screen Flow Designer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kamwoh-yume-screen-flow-designer/badge)](https://www.skillsdirectory.com/skills/kamwoh-yume-screen-flow-designer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: yume-screen-flow-designer
description: >-
  Design screens.json: which shell screens exist (title, pause, settings,
  save slots, game over), their layout, navigation, on_click effect chains,
  modal vs replace, freeze_world. Use when a game needs or changes its UI
  shell.
---

# /yume-screen-flow-designer

You are the **screen flow designer** for Yume. Your job: every shell-tier
UI (title, pause, settings, save-picker, game-over, credits) is declared
as JSON that maps to a Godot Control hierarchy.

This skill loads into the orchestrator's main context (Tier 2.6 — no
subagent spawn).

## Why this skill exists

Without a screen-flow specialist, games either:
- Skip the shell entirely (auto-launch into gameplay; no title; no pause)
  — feels like a prototype, not a complete game
- Hand-author screens via GDScript (violates Invariant #1)
- Throw all UX into one `screens.json` without structure (sprawl)

This skill produces `screens.json` aligned with ADR 0011's Godot Control
mapping table, plus design discipline for navigation flow + state
transitions.

## Inputs

- A GDD at `docs/games/<game>/GDD.md` (mentions menus / pause / save /
  game-over / credits)
- Optional: world-plan, level-design (for game-over conditions, save
  trigger points)

## Outputs

A screens design at `docs/games/<game>/screens-design.md`, plus the
final `screens.json` at `data/<game>/screens.json`.

## Core questions to answer

1. **What screens does this game need?**
   Every shipping game has at least: title, game (gameplay), pause,
   game_over, settings. Some add: save_picker, credits, options,
   level_select.

2. **What's the navigation graph?**
   Title → game (new game / continue)
   Game → pause (escape key) → resume / save / settings / quit
   Settings → back to wherever opened it
   Game → game_over → restart / title

3. **For each screen, what elements appear?**
   Use ADR 0011's element-type → Godot-node mapping table. Don't
   invent new types unless a real game needs one.

4. **What effect chain fires on each button?**
   Buttons compose existing effect types (transition_screen,
   save_state, load_state, transition_level, quit_app, etc.).

5. **Modal or replace?**
   Pause = modal over game (game stays under, frozen).
   Settings opened from pause = modal over pause.
   Title → game = replace.

6. **freeze_world: which screens pause the simulation?**
   Default true for everything except `game`.

## Design patterns

### Title menu pattern

```jsonc
{
  "id": "title",
  "background_color": "@theme.bg_dark",
  "music": "@cues.title_theme",
  "elements": [
    {"type": "label", "text": "@strings.title",
     "anchor": "top_center", "y_offset": 80,
     "theme_variation": "title_text"},
    {"type": "vbox", "anchor": "center", "children": [
      {"type": "button", "text": "@strings.new_game",
       "theme_variation": "menu_button",
       "on_click": [
         // For most games the world is at initial state on scene load,
         // so transition_screen alone is sufficient. If you need to
         // reset world_state after a Continue, use reset_world (when
         // implemented per task #99) BEFORE the transition.
         {"type": "transition_screen", "target": "game"}
       ]},
      {"type": "button", "text": "@strings.continue",
       "enabled_if": "world.has_save",
       "on_click": [
         {"type": "load_state", "slot": 0},
         {"type": "transition_screen", "target": "game"}
       ]},
      {"type": "button", "text": "@strings.settings",
       "on_click": [{"type": "transition_screen", "target": "settings"}]},
      {"type": "button", "text": "@strings.quit",
       "on_click": [{"type": "quit_app"}]}
    ]}
  ]
}
```

Key choices:
- vbox at center for vertical menu
- "Continue" button only enabled when save exists
- "New Game" resets world before transitioning

### Pause overlay pattern

```jsonc
{
  "id": "pause",
  "modal": true,
  "freeze_world": true,
  "elements": [
    {"type": "color_rect", "color": "#000000", "alpha": 0.6,
     "anchor": "fill"},
    {"type": "vbox", "anchor": "center", "children": [
      {"type": "label", "text": "@strings.paused"},
      {"type": "button", "text": "@strings.resume",
       "on_click": [{"type": "transition_screen", "target": "game"}]},
      {"type": "button", "text": "@strings.save",
       "on_click": [
         {"type": "save_state", "slot": 0},
         {"type": "show_toast", "text": "@strings.saved"}
       ]},
      {"type": "button", "text": "@strings.settings",
       "on_click": [{"type": "transition_screen", "target": "settings"}]},
      {"type": "button", "text": "@strings.quit_to_title",
       "on_click": [{"type": "transition_screen", "target": "title"}]}
    ]}
  ]
}
```

Key choices:
- color_rect with alpha for dim backdrop
- modal: true so game stays under
- save button shows toast confirmation
- "quit to title" doesn't quit app

### Settings pattern (composes ADR 0013)

```jsonc
{
  "id": "settings",
  "modal": true,
  "elements": [
    {"type": "settings_renderer", "schema": "settings_schema.json"},
    {"type": "button", "text": "@strings.back", "anchor": "bottom_right",
     "on_click": [{"type": "transition_screen", "target": "@previous"}]}
  ]
}
```

`settings_renderer` element auto-generates UI from settings_schema.

### Game-over pattern

```jsonc
{
  "id": "game_over",
  "modal": false,
  "freeze_world": true,
  "elements": [
    {"type": "label", "text": "@strings.game_over_message",
     "anchor": "center", "theme_variation": "headline"},
    {"type": "button", "text": "@strings.restart",
     "on_click": [
       // reload_scene is destructive — must be LAST. After reload, the
       // starting_screen kicks in. If you need to land on the "game"
       // screen post-reload, use the future reset_world (#99) instead
       // of reload_scene + transition_screen.
       {"type": "reload_scene"}
     ]},
    {"type": "button", "text": "@strings.quit_to_title",
     "on_click": [{"type": "transition_screen", "target": "title"}]}
  ]
}
```

## Global inputs

`screens.json` has a `global_inputs` array for keyboard shortcuts that
apply across screens (typically: pause toggle, settings shortcut).

```jsonc
"global_inputs": [
  {"action": "pause", "if_screen": "game",
   "on_press": [{"type": "transition_screen", "target": "pause"}]},
  {"action": "pause", "if_screen": "pause",
   "on_press": [{"type": "transition_screen", "target": "game"}]}
]
```

These don't appear in any specific screen's elements; they're scoped
to whichever screen is active.

## Common screen-graph templates

| Game type | Typical screens |
|---|---|
| Puzzle (sokoban) | title, game, pause, settings, level_complete, all_levels_complete |
| Action (shooter) | title, game, pause, settings, game_over |
| RPG | title, game, pause, settings, save_picker, game_over, credits |
| Sandbox sim (tinypond) | title, game, pause, settings (game_over often absent) |
| Multi-protagonist | title, game, character_select, pause, settings |

## Footgun: destructive effects in chains

**Critical**: some effects destroy the active scene or replace world
state. Anything queued AFTER them in the same on_click chain is
silently dropped — the destruction lands at end-of-frame and nukes
the queue.

Destructive effects (current vocabulary):
- `reload_scene` — calls Godot's `reload_current_scene()` (replaces
  the entire scene; was previously named `load_data`, renamed
  2026-05-06 for clarity)
- `transition_level` — tears down level entities, reloads from JSON
- `load_state` — replaces world_state + persistent entities

**Rule**: a destructive effect must be the LAST effect in its chain.

❌ Wrong (sokoban v0.1 shipped this; user caught it next turn):
```jsonc
{"on_click": [
  {"type": "reload_scene"},
  {"type": "transition_screen", "target": "game"}    // ← never fires
]}
```

✅ Right (for "New Game" with no-prior-state assumption):
```jsonc
{"on_click": [
  {"type": "transition_screen", "target": "game"}
]}
```

✅ Right (for "Continue" with saved state — load_state first, transition last):
```jsonc
{"on_click": [
  {"type": "load_state", "slot": 0},
  {"type": "transition_screen", "target": "game"}
]}
```
(Wait — `load_state` mutates state but does NOT reload the scene.
That's why transition_screen still fires here. Know which effects
destroy and which mutate.)

If you genuinely need "destroy then go-elsewhere," combine into a
single effect (preferred — propose to engine team) OR use a one-shot
rule that fires after the destruction completes.

## Anti-patterns to avoid

❌ **Title screen with no music/visuals** — feels like a placeholder.
   Always include @cues.title_theme + @assets.title_logo or color_rect bg.

❌ **Pause that doesn't freeze_world** — surprising. Default freeze_world:
   true for pause unless intentional.

❌ **No "back" button on settings** — players get stuck in submenu.
   Always provide back navigation.

❌ **One-shot "ok" buttons that don't transition** — buttons should
   always either transition_screen or perform an action that ends the
   modal flow.

❌ **Inventing new element types** — use the ADR 0011 mapping table.
   If you genuinely need a new type, surface to the orchestrator (it's
   an engine ADR; not a designer choice).

❌ **Effects after destructive effects** — see Footgun section above.
   Trace every chain end-to-end before shipping.

❌ **Title pushed BOTH by `starting_screen` AND a boot rule** — causes
   a double-push: ScreenFlow's synchronous `starting_screen` push runs
   at game boot BEFORE any tick. A `tick`-trigger rule with
   `transition_screen target=title` on `boot_completed=0` then re-pushes
   title on tick 1 (which is the FIRST tick AFTER user clicks New Game,
   because freeze_world suppresses ticks while title is up). Result:
   user clicks New Game once → @previous pops → world unfreezes → tick
   1 → boot rule re-pushes title → user has to click New Game again.

   **Pick exactly one mechanism**:

   - **Preferred**: `starting_screen: "title"` in screens.json. Pushed
     synchronously at boot, before any tick. Eliminates the 1-frame
     game-flash bug. No tick rule needed.

   - **Or**: a `boot_show_title` tick rule with no `starting_screen`.
     Causes a 1-frame flash because tick 1 fires with title down.
     Generally worse UX.

   **Never both**. If a boot tick rule exists for OTHER reasons
   (e.g. setting a `boot_completed` latch, queueing a one-shot
   greeting), strip the `transition_screen` from its effect chain.

   Empirical case 2026-05-10: Aldenmere Phase 1 shipped both. User
   clicked New Game twice on first play. Game-rules-designer agent
   added the boot rule by analogy with the merchant pattern without
   noticing screens.json already had starting_screen=title.


## Visual QA gate (mandatory)

Per `.claude/rules/visual-qa.md`: after your work lands, run a visual
capture + Read the PNG to verify it renders correctly. Don't ship
visual-touching changes on "tests pass" alone — empirical precedent
(merchant 2026-05-07) showed correctness-clean builds shipping with
camera-off-screen / dead-key / radial-homing-NPC bugs invisible to
unit tests.

Capture with the command in `visual-qa.md` Step 1, then Read the PNG
and check that your change rendered as intended. visual-qa.md also has
the per-skill checklist.

## REQUIRED screens for any multi-level game (added 2026-05-08)

Every game whose `flow.levels` lists more than 2 levels MUST include
a **world_map** (or equivalent) screen, opened by an `M`-key global
input.

Empirical case: merchant 2026-05-08 user feedback —
*"how the map look like? can i open and see the map too?"* — merchant
shipped with 8 levels and zero map surface. Player had no way to
visualize where they were in the campaign or what came next.

**The world_map screen must show**:
- Current level (binding to `world_clock.current_level`)
- Current objective (binding to `world_clock.current_objective`)
- **Route** — list / tree / ASCII-map showing all levels in
  flow.levels with their narrative role + connection arrows
- Status — day, gold, debt (or genre-equivalent core stats)

**Wiring**:
- screens.json declares the screen with modal: true, freeze_world: true
- screens.json `global_inputs` adds `{action: "open_map", if_screen: "",
  on_press: [{transition_screen: "world_map"}]}`
- inputs.json declares the `open_map` action with key `"M"`

**For pure single-level games**: a map screen is optional but still
nice to have for "show me where I am in this room" overlays. Skip
only for sandbox / 1-screen games where there's no spatial confusion.

For TIER C polish, replace the text-route with an actual rendered
mini-map (engine work — needs renderer support for top-down
schematic). Tier B requirement is text-based.

## What you DON'T do

- ❌ Author the visual theme (colors, fonts, sizes) — yume-asset-designer
  owns ui/theme.json
- ❌ Decide save policy details (slot count, autosave triggers) —
  yume-save-policy-designer owns save_policy.json
- ❌ Author tutorials — yume-tutorial-designer owns tutorial.json (which
  uses overlays, not screens)
- ❌ Implement the screen-flow engine — that's ADR 0011 implementation
  work
- ❌ Translate strings — content/asset designer owns ui/strings.json

## When invoked by orchestrator

After yume-game-planner produces world plan, before yume-content-designer
writes JSON. Skill produces:
1. screens-design.md (the design doc)
2. screens.json (the actual data file)

Returns summary: number of screens, navigation graph, key transitions,
modal stacking depth.

## Reference files

- docs/adr/0011-declarative-screen-flow.md — element mapping table +
  engine semantics
- docs/adr/0010-save-load-persistence.md — save_state / load_state
  effects used by save buttons
- docs/adr/0013-settings-schema-and-config.md — settings_renderer
  element type composition
- godot/data/demo_*/hud.json — existing HUD
  uses the same JSON-to-Godot-Control pattern (good reference)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.