game-session-host
Hosts a game module through one typed seam: game screen, pause, undo, hints, a rescue, saved runs, results.
Skill by zakariaf
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/zakariaf-game-session-host
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Node.js package
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/game-session-host
What's inside
- Skill name
game-session-host- Description
- Hosts a game module through one typed seam: game screen, pause, undo, hints, a rescue, saved runs, results. Use when assembling a game or wiring the Game screen or registry. Not for rules (game-rules-engine).
- License
- MIT
- Instructions
- 230 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (113)
- SKILL.md
- shared.json · assets/
- game-feedback.ts · examples/app-wiring/
- game-host-config.ts · examples/app-wiring/
- game-registry.ts · examples/app-wiring/
- games-root.tsx · examples/app-wiring/
- lights-out-hosted.ts · examples/app-wiring/
- lights-out-tutorial.ts · examples/app-wiring/
- game-route.tsx · examples/game-screen/
- game-screen.tsx · examples/game-screen/
- use-game-model.test.tsx · examples/game-screen/
- use-game-model.ts · examples/game-screen/
- sqlite-game-runs-repository.test.ts · examples/sqlite-game-runs/
- sqlite-game-runs-repository.ts · examples/sqlite-game-runs/
- v0002-create-game-runs.ts · examples/sqlite-game-runs/
- assembling-a-game.md · references/
- host-architecture.md · references/
- session-flow.md · references/
- check-game-host.mjs · scripts/
- check-lib.mjs · scripts/
- app-modules.mjs · scripts/lib/
- host-layout.mjs · scripts/lib/
- host-probes.mjs · scripts/lib/
- host-static.mjs · scripts/lib/
- source-scan.mjs · scripts/lib/
- zustand-stand-in.mjs · scripts/lib/
- selftest.mjs · scripts/
- run-ref.test.ts · templates/src/core/game-results/
- run-ref.ts · templates/src/core/game-results/
- difficulty.test.ts · templates/src/core/game/
- difficulty.ts · templates/src/core/game/
- game-engine.ts · templates/src/core/game/
- game-input.ts · templates/src/core/game/
- game-module.ts · templates/src/core/game/
- game-rules.ts · templates/src/core/game/
- daily-start.test.ts · templates/src/core/game/level-kit/
- daily-start.ts · templates/src/core/game/level-kit/
- star-rating.test.ts · templates/src/core/game/level-kit/
- star-rating.ts · templates/src/core/game/level-kit/
- levels.ts · templates/src/core/game/
- messages.ts · templates/src/core/game/
- persistence.ts · templates/src/core/game/
- stats.ts · templates/src/core/game/
- testing.ts · templates/src/core/game/
- contract-intents.test.ts · templates/src/core/game/testing/
- contract-intents.ts · templates/src/core/game/testing/
- engine-contract.test.ts · templates/src/core/game/testing/
- engine-contract.ts · templates/src/core/game/testing/
- json-shape.test.ts · templates/src/core/game/testing/
- json-shape.ts · templates/src/core/game/testing/
- play-bot.test.ts · templates/src/core/game/testing/
- play-bot.ts · templates/src/core/game/testing/
- play-choices.test.ts · templates/src/core/game/testing/
- play-choices.ts · templates/src/core/game/testing/
- fake-game-runs-repository.test.ts · templates/src/data/game-runs/
- fake-game-runs-repository.ts · templates/src/data/game-runs/
- game-runs-repository.ts · templates/src/data/game-runs/
- use-pause-on-background.test.tsx · templates/src/hooks/
- use-pause-on-background.ts · templates/src/hooks/
- board-host-model.test.ts · templates/src/services/game-host/
- …and 53 more
SKILL.md (first lines)
# Game session host
Makes every game playable inside the app the same way: the typed game module enters through one
generic seam (`createGameHost`), each run lives in a per-run store that writes the run before the
board animates it, a finished run is recorded in one repository transaction before its result
shows, and screens see only plain data and commands. The skill ships the host (templates), the
wiring around it (examples) and a checker that proves the invariants by running the app's own host
modules against a tiny test game, on iOS and Android alike (the host is pure TypeScript and React).
## Rules that must hold
1. **`createGameHost(game, deps)` is the only place the app touches a game's own types.** Screens,
routes, UI, stores and shared hooks use the erased `GameHost`, `SessionHandle` and
`SessionView`; only the registry (`src/games/game-registry.ts`) names a game, one factory per
game; no cast anywhere in the seam. Why: TypeScript cannot hold "a module of some type" without
a cast (strictFunctionTypes makes the intent parameter contravariant), and a cast hides exactly
the errors the compiler exists to catch. (`unsafe-cast`, `screens-type-erased`)
2. **Every change is reduced, persisted, then published.** The per-run store calls its persist step
before `set()`; a failed write publishes nothing and the view shows `saveFailure` so the screen
offers a retry. Why: iOS and Android end background apps without warning; a kill during an
animation must reopen the exact state on screen. (`save-before-publish`, `save-failure-shown`)
3. **Only the game's `intentToMove` makes moves; `null` means nothing happens.** The reducer ignores
moves while paused or decided, and undo never steps out of a loss; screens send commands (undo,
hint, continue, pause, finish, leave), never moves. Why: bots, solvers and players share one
legality, and no illegal state can be saved. (`intent-legality`, `moves-only-while-playing`,
`undo-not-after-loss`)
4. **A run ends in ONE repository transaction, before its result shows:** the result row, the best,
the statistics and counters, and the saved run cleared (`recordRunEnd`). The move that decided
the run then plays the win or lose feedback once. Why: a result on screen that is not on disk
breaks trust, streaks and statistics. (`run-end-once`, `result-feedback`)
5. **At most one rescue per run, only after a loss, only when the rules have one and the app's config
allows it** (`src/config/game-host-config.ts`, never a literal). A rescuable loss stays saved and
unrecorded until the player continues or declines (`finish`); a new run of the same game first
records a decided saved run (declined) and, if that write fails, writes nothing. Why: a Continue
the rules cannot honour does nothing, and an abandoned loss must still reach results and streaks.
(`continue-once`, `rescue-pending`, `abandoned-run-recorded`, `config-rules`, `continue-from-config`)
6. **Never strand a finished run.** A pending loss whose rescue offer is `'hidden'` (ads off,
offline, no consent, no Premium) is finished at once: the Game screen's model sends
`{ type: 'finish' }` once per `eventSeq` when `isLossStranded(view, offer)`; a loading ad is
`'loading'`, never hidden. Why: an endless run in an ads-off build otherwise never shows its
result and is never counted. (`loss-not-stranded`, `game-screen-wiring`)
7. **A saved run is validated by the game's own parsers at boot and on resume.** An unreadable run is
dropped alone and logged (results stay); a replay that differs from the snapshot keeps the
position and drops only the undo history; a resumed run starts paused. Why: one damaged row must
never cost progress or crash the app. (`saved-run-validated`)
8. **Hosts are created once, in the composition root, after the data layer opened, never at import
time or in a render** (a `useState` initializer), and provided once (`GameHostsProvider` inside
the navigation theme). Opening a run writes nothing (dropping an unreadable saved run is the one
idempotent exception); its handle `begin()`s in `useGameSession`'s effect.
Why: a host reads and validates saved runs, and a render React repeats (StrictMode) must write
nothing. (`host-not-created`, `host-at-import`, `host-in-render`, `host-not-provided`)
9. **The Game screen opens its run with `useGameSession`, renders the host's `BoardHost`, pauses on
background, blur or a full-screen ad, and guards Back with `backCommandOf`.** iOS reports
`inactive` (app switcher, calls) and keeps `active` under a Google full-screen ad; Android
reports only `background`; all pause, and Pause shows afterwards on both. Back (header, iOS
swipe, Android button, predictive back) is caught with `usePreventRemove` while the run is live
and unrecorded: pause in play, resume in Pause, finish a pending loss; Pause -> Home sets a
leaving flag first so the guard lets that one removal through. Why: coming back must show Pause,
and only Pause -> Home leaves a live run. (`game-screen-wiring`, `back-guard`)
10. **Paid perks are sent only after payment, and offered only when they can be given:** a hint or a
continue is sent when the rewarded ad completed, the allowance was spent or the player owns
Premium (the monetization layer's `pay(then)`); the hint is offered only while
`view.isHintAvailable`. Why: a perk granted before the ad completes is free forever, and a paid
hint with nothing to show is wasted. (Proven by the Game screen's model test.)
11. **A tap-then-tap selection is UI state of the board host, never a move.** A tap in one of
`input.selectRegions` toggles it, the next tap reaches `intentToMove` with `selected` filled in,
and any new move, undo, rescue, run end or drag clears it. Why: bots, solvers, par, undo and the
counters must never see a selection. (`board-host-model.test.ts`; `intent-legality`)
12. **Stars come from the level's own star rule, counters from the kept moves, play time only while
playing** (each step clamped to 0..300,000 ms), and the first finished attempt of a daily is the
day's result: a replay is practice (`isPractice`: no new best, no statistics, no streak). Why:
undone moves must not count, a phone asleep on the result screen must not add hours, and a
replayed daily must not rewrite the day. (`stars-from-table`, `counters-from-log`, `play-time`,
`daily-practice`)
13. **Each game is one binding, registered, with its options and save policy right:**
`src/games/<id>/<id>-hosted.ts` is `{ ...<ID>_GAME, Surface[, tutorial] }` typed `HostedGame`,
the registry hosts it, its options promise nothing its rules lack, a game with a real-time loop
(`sim/`) saves at save points and a turn-based one after every move, and
`contracts/architecture.json` `gameFacing` lists the seam. Why: the binding is the one place the
app types a game. (`hosted-binding`, `game-registered`, `save-policy`, `game-facing-recorded`)
14. **A real-time game saves through the host at its save points:** its board calls
`onSavePoint(realtime.snapshot(copy), inputLog)` at each save point and at the run's end; the host
writes it through the same persist step (never per move) and records the run once when that
state decides it. Why: a direct write would bypass the store and the run-end transaction.
(`save-point-write`)
15. **A tutorial (optional) plays its script beside the saved run, in memory:** each step accepts
only its expected move, nothing is written or recorded, and every step is legal from its start.
Why: replaying the tutorial must never cost the player their run or count in statistics.
(`tutorial-beside-run`, `tutorial-legal`)
16. **Test-build shortcuts end runs through the same persist step as real play** and reach only the
debug layer (`host.debugControls()`), never store builds. Why: an E2E flow must see the real
run-end write and the real result. (`debug-run-end`)
## Workflow
1. **Check what exists.** Run `node ${CLAUDE_SKILL_DIR}/scripts/check-game-host.mjs .`. It prints
NOT APPLICABLE until the app has a game. The game's rules and module come first
(game-rules-engine: `src/core/game/` and `src/games/<id>/<id>-game.ts`); levels make level,
daily and endless runs possible (level-generation-and-solvers). Read
[references/host-architecture.md](references/host-architecture.md) for the seam and the files.
2. **Install the host once per app.** Copy `templates/` into the repo at the paths line 1 of each
file names: `src/services/game-host/`, `src/stores/game-session/`, `src/data/game-runs/`
(the port and its fake), `src/core/game-results/`, `src/hooks/use-pause-on-background.ts` and
`test/games/` (the tally test game). `templates/src/core/game/` holds synced copies of
game-rules-engine's kit and level-generation-and-solvers' `level-kit/star-rating.ts` and
`level-kit/daily-start.ts`: keep the copies the app already has. Run `npm run typecheck` and
`npx jest src/services/game-host src/stores/game-session src/data/game-runs --ci --coverage --coverageThreshold='{}'`.
3. **Store runs in the data layer.** Add a SQLite `GameRunsRepository` from
`examples/sqlite-game-runs/` and hand it down with the app's data (`useAppData()`). Its migration
is a new schema version (persistence-and-migrations, "Adding version N+1"): register the step,
then add that version's frozen fixture database, its checksum and its `FIXTURES` entry, or the
"every shipped version has a fixture" test fails.
4. **Wire the composition.** Read "The composition" in host-architecture.md. Write
`src/games/game-registry.ts`, `src/config/game-host-config.ts` and the games root from
`examples/app-wiring/`, and mount `GamesRoot` inside the navigation theme (app-startup-and-
bootstrap's provider order). Prove the providers with one render test through the real root.
5. **Assemble each game.** Read [references/assembling-a-game.md](references/assembling-a-game.md):
the binding (`examples/app-wiring/lights-out-hosted.ts`), its registry factory, its options from
its rules, its save policy, its optional tutorial (`examples/app-wiring/lights-out-tutorial.ts`),From zakariaf/ReactNative-Skills, MIT.
More from zakariaf/ReactNative-Skills
- 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
android-release-play
zakariaf
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
architecture-and-boundaries
zakariaf
Places app code in the src/ layout and checks import boundaries: one-way zones, pure core, ports with fakes, one composition root.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
async-safety
zakariaf
Prevents silent async bugs: effect races, stale results, lost promises, leaked listeners and timers, AbortSignal gaps.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
audio-and-haptics-feedback
zakariaf
Builds SFX and haptics behind ports.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
auth-and-secure-storage
zakariaf
Builds sign-in - tokens in expo-secure-store, shared refresh, protected routes, OAuth PKCE, Apple sign-in.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
background-tasks
zakariaf
Builds expo-background-task jobs: idempotent, budgeted runs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9
Signals
- Stars
- 11
- Forks
- 2
- Open issues
- 0
- Last commit (this folder)
- Oct 17, 2026 (11 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/zakariaf/ReactNative-Skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from zakariaf
- 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9
Cite this page
game-session-host skill. Agent Mart. https://agentmart.dev/skills/zakariaf-game-session-host (last updated 2026-10-24)