index.php — Game Briefing (Step 1)
The one screen where the user hands the assistant a brief. Everything after this — resources, card types, decks, cards, art — is the AI working from what was captured here.
What this page is, conceptually
Onboarding is the creative brief handoff. Think of it the same way a client hands a brief to a designer: the client doesn't need to know how the deliverable is made, they just need to describe the game they want well enough that the designer (here, the AI) can start working.
That framing explains every design choice on the page:
- Single page, no wizard. A brief is a document, not a checklist. You fill it in one sitting.
- Required vs optional. The required section is the minimum the assistant needs to produce anything coherent downstream. Optional fields raise the ceiling on quality.
- An "IA Suggestions" button on every field. This is the assistant answering the question "what should I put here?" — the same way a human designer might say "hey, want me to pitch a few taglines?"
- A completeness meter. A friendly nudge that says "the more you tell me, the better I can design."
The mental contract is: once the briefing is saved, the AI has enough to start designing your game. Everything from Step 2 onwards draws on this document — which is why bugs where fields silently disappear are worse than they look: they poison every downstream AI call.
The user's journey on this screen
-
Arrival. User clicks "New Game" in the sidebar (or "+ New
Game" from
index-games.php). They see a single-card form with a "Game Briefing" header and a completeness meter showing0 / 7 required. - Core block (required). Name, one-line pitch, mechanic, player mode, game size, player count. This is the "what kind of game is this?" question — no answers, no game.
- Flavor block (optional). Extended description, inspirations, goal, world/setting, graphic style. This is "what does your game feel like?" — each answer sharpens the AI's voice.
- Business block (collapsed). Product model, team play. Rarely the first thing on a designer's mind, so it's hidden until they ask for it.
- Per-field assists. At any point the user can click "IA Suggestions" next to a field. The AI reads everything else they've written so far and proposes options. The user clicks a suggestion and it lands in the field.
- Save. A modal appears ("That's a great start! Please wait while we initialize everything"). Behind the scenes, the game is created, the briefing is stored, and the AI is given its opening context. When that's done, the user is offered a "Continue" button to Step 2.
ALT-clicking an "IA Suggestions" link opens the raw AI response in a new window instead of rendering it inline. It's undocumented, but it's a developer debug hatch worth keeping.
What the form is really capturing
It's more useful to think about the briefing in terms of the questions the AI needs answered rather than the field names. Grouped by intent:
Identity — “What is this game called and what's the hook?”
game_name, elevator_pitch
The name anchors the AI's voice. The pitch forces the user to commit to a single sentence about why someone would want to play this game. Both are required because every downstream AI prompt opens with them.
Shape of play — “How does this game actually work?”
mechanic, subgenre, player_mode, player_count_min/max, is_team_game
This is the mechanical skeleton. The mechanic (deck-builder, TCG duel, draft, set-collection, trick-taking) should drive almost every downstream decision: which card types make sense, what zones to set up, what deck sizes are idiomatic. Today it doesn't — the AI prompt is hardcoded to "2-player attack/defense deck-building" regardless of what the user picked. Closing that gap is the single highest-leverage change on the roadmap.
Subgenre is currently only populated for Deck-Builder; other mechanics have no subgenre list yet.
Scale — “How big is this game going to be?”
card_volume_preset (+ derived card_volume_data)
Three presets (Small / Medium / Large) translate into a soft and hard cap on total cards. Everything in Step 3 (decks + per-deck soft caps) derives from this number. Custom lets power users set their own caps.
Conceptually this is a print-scale decision, not a mechanical one — it's really the answer to "how thick will the box be?" Which is why it's natural to extend it later with card size (poker / bridge / tarot) and copy count.
Flavour — “What does this game feel like?”
game_description, game_goal, game_imaginary (world), game_inspired_in, game_graphic_style
These are the fields that turn "a deck-builder for 2" into a specific game. Optional, because a mechanic-first designer might not have a world yet — but the more filled in, the more the AI's suggestions feel like your game instead of a stock template. The "each field improves suggestion quality" helper text already says exactly this.
Business — “How is this game going to be published?”
product_model, is_team_game (disabled)
Collapsed because most users won't think about it on day one. The "Digital-Only" option here is a legacy leftover — it contradicts the print-first product goal and should be removed or rebranded as "Print + digital companion". Team play is explicitly "coming soon" in the UI.
What happens when they hit Save
In plain language:
- A SweetAlert spinner appears so the page feels busy.
- The form posts to
ajax-steps.php?method=init. -
The server decides: is this a new game (no
gameUniqueIdyet) or an edit of an existing one?- New: mint a
unique_id(MD5 of name + microtime), insert a row ingame_info. - Edit: update the game's name if it changed.
- New: mint a
-
The briefing is assembled from the POST fields and written to the
game_briefingkey in the meta store. -
The server compares the new briefing to the old one. If anything
changed (or there's no prior AI conversation yet), it calls
provideContextToIA, which sends the AI a single "here is the game you're helping me design" message. That kicks off the long-running conversation that all future AI calls will append to. -
The server returns
{ success, gameUniqueId }. The browser offers a "Continue" button which navigates toindex-step2.php.
- If the briefing didn't change, the server still
sleep(5)s unconditionally, purely to keep the spinner visible. Worth replacing with real progress. - If it did change, the server pads the AI call to a minimum of 5 seconds, which hides fast responses behind artificial latency.
How the briefing feeds the rest of the app
Every screen after this one opens with a call to
getGameMeta($conn, $gameUniqueId, 'game_briefing') and a call
to buildGameContextPromptFromId(). So the briefing is effectively
read on every single page load after Step 1.
It shows up in three main places:
- Page preamble. Step 2 / 3 / 4 display the game name, description, card-volume totals, etc. This is purely read-only.
- Derived defaults. Step 3 (decks) divides the card-volume soft cap across decks using ratios that assume a deck-builder shape. Step 4 (cards) uses the card-volume to decide how many cards to generate.
- AI context. Every AI call — per-field suggestions, card-group suggestions, bulk card generation — begins with a prompt built from the briefing. If a field wasn't captured, the AI doesn't know about it.
That last bullet is why the "silent data loss" bugs are costly: a missing field here isn't a single-page inconvenience, it's every future AI call being dumber than it could be.
Design choices that serve the goal well
- One-page brief, not a wizard. Matches how creatives actually think. Stepping through "what's the name / → next / what's the pitch / → next" would feel like a tax form.
- Required / Optional / Advanced tiers. Gives a novice a clear path to "done" while keeping depth available for power users.
- Per-field AI suggestions. The single most important primitive for a creative tool — it attacks the blank-page problem at exactly the moment the user is stuck. It also happens to be the place where the product delivers its core value (AI-assisted design) most directly.
- Completeness meter. Tiny feature, big coaching effect. It tells the user "you're 60% of the way to giving me what I need" without nagging.
- Card-volume presets as the scale knob. The right abstraction: users don't think in "card counts", they think "small casual game" vs "deep strategic game".
- Data-driven taxonomy. Mechanics, subgenres, player modes, product models all come from JSON, not hardcoded in PHP. Easy to expand.
Gaps between this page and the print-first goal
The goal is an assistant that designs printable, custom card-based board games. Measured against that, here's what this screen doesn't yet ask about:
Nothing is printed-aware yet review later
The user might reasonably leave this page thinking they're making a digital game. Nothing tells them "we'll be printing this". Missing inputs that a real print-first assistant will need:
- Card size: poker, bridge, tarot, mini-Euro — drives every downstream layout decision.
- Expected copies / print run: 1 (personal) vs 50 (small batch) vs >500 (commercial) has different cost/quality implications.
- Language(s): affects text length budgets on cards.
- Box / delivery format: tuck box, two-piece, shrink-wrap only, etc.
Deferred. Not addressed now — print specs mostly bite at export / layout time, so it's more natural to capture them closer to that stage than on the onboarding brief. Revisit when the print-output pipeline starts taking shape.
Audience / complexity context added
A family game for 8-year-olds and a hobbyist game for 40-year-old Eurogame fans need completely different AI suggestions. The brief now captures three audience signals, all optional:
- Age rating — 6+ / 8+ / 10+ / 12+ / 14+ / 18+
- Play time — presets from “under 15 min” through “over 120 min”
- Complexity — 1–5 BoardGameGeek-style weight, from “light (pick up and play)” to “heavy (hobbyist)”
Each field feeds the AI via a small helper
(buildAudienceFragmentFromBriefing) that appends a natural-language
fragment like “Targeted at ages 12+, 30–60 minutes per session, medium
complexity.” to every live prompt. They're also counted in the
completeness meter and persisted in game_briefing.
Mechanic now steers the AI fixed
Previously, every AI call opened with the hardcoded sentence “You are helping create a 2-player attack/defense deck-building card game based on turns...”, regardless of the mechanic the user picked. A trick-taking game got the deck-builder framing, too.
This was fixed by adding a helper
buildGameFramingFromBriefing($briefing) in
inc-functions-final.php that derives a natural-language
phrase from the briefing (player count + player mode + mechanic +
optional subgenre):
"2-player competitive combat deck-builder card game"
"3-5 player cooperative set collection card game"
"1-player solo trick-taking card game"
All three live call sites now use the helper: the briefing-suggestion
prompt, the initial context seed (provideContextToIA), and
the shared buildGameContextPrompt used by the Step 2
resource suggestions. The nested "3 deck-building games for inspiration"
line in the briefing-suggestion case is now phrased from the mechanic
too ("3 trick-taking card games for inspiration", etc.).
Remaining: the per-method prompts in ajax-ia-suggestions.php
(zones, rules) still assume 2-player symmetrical flow. Each is its own
prompt that needs mechanic-aware phrasing — a follow-up once those
flows are re-enabled in the UI.
No "briefing review" step
Right now: user submits, server seeds AI context, user is kicked to Step 2. At no point does the AI say "here's the game I think you described — shall we proceed?". That's a natural beat, catches bad briefings before the whole deck is generated on top of a wrong foundation, and is exactly the kind of moment where the product feels like an assistant instead of a form.
The briefing can now be edited fixed
Previously, loading index.php?gameUniqueId=XXX gave you a
blank form because $gameData was never populated from the
DB, and index-games.php had no link that went back to Step 1
anyway. The briefing was effectively a one-shot decision.
Fixed: index.php now loads
$gameData = getGameMeta($conn, $uniqueId, 'game_briefing')
at the top, so the form prefills when a game ID is passed. The save
path already handled updates correctly, so editing now round-trips
cleanly.
index-games.php was also upgraded (see below) into a
DataTable-backed manage view with per-step edit links
(Briefing · Pieces ·
Decks · Cards), so the
entry point into each stage is now explicit.
Verified bugs
Short list, since the conceptual analysis above is the main story. Items marked fixed are done.
| Bug | Status | Notes |
|---|---|---|
Form fields elevator_pitch, mechanic, subgenre, player_mode, product_model were not persisted |
fixed |
ajax-steps.php init handler now writes all 15 form fields
to game_briefing. The legacy
game_genre_subgenre key (still read by
inc-ai-context-functions.php) is synthesised from
mechanic/subgenre for back-compat.
|
$gameData not initialised when loading the page |
fixed | Now prefilled from getGameMeta($conn, $uniqueId, 'game_briefing') at the top of index.php. Reloading with ?gameUniqueId=XXX shows the stored brief. |
FILTER_SANITIZE_STRING (line 26) |
open | Deprecated in PHP 8.1, removed. Replace with a whitelist regex for unique_id. |
No validation that player_count_min ≤ player_count_max |
open | Client and server both accept invalid ranges. |
| AI system prompt hardcoded to “2-player attack/defense deck-builder” | fixed |
New helper buildGameFramingFromBriefing() in
inc-functions-final.php derives the framing from the
briefing. Applied to the 3 live call sites
(buildGameContextPrompt, provideContextToIA,
and the briefing-suggestion case). See
Mechanic now steers the AI above.
|
| ~500 lines of dead JS on this page | open | showStep(), .use-this-resource, card-family toggles, commented-out resource submit — all belong to Step 2 or legacy. |
TODOs
Checkbox state is saved locally per page.
Recently fixed
-
Persist all 15 form fields in
game_briefingDone inajax-steps.php. Verified against the form inindex.php. -
Template the AI system prompt from the briefingNew helper
buildGameFramingFromBriefing()ininc-functions-final.php; applied inbuildGameContextPrompt,provideContextToIA, andajax-ia-suggestions.php'sbriefing-suggestioncase (including the "3 similar games for inspiration" line). -
Delete confirmed-dead code in the AI layerRemoved
buildGameContextPromptFromId()and 6 deadcasebranches inajax-ia-suggestions.php(life-points-suggestion,cardgroup-suggestion,cardtype-description-suggestion,provide-resources-context,provide-card-config-context, unreachableprovide-initial-context). File shrank from ~694 to 454 lines. -
Installer / health-check page
install.phprewritten as a read-only status dashboard with explicit install actions;inc-db.phpnow honours$SKIP_AUTO_CONNECTso the UI renders even when the DB is unreachable. -
Add audience & complexity fields to the briefAge rating, play time, complexity (1–5) added as a new optional "Audience" section on
index.php. Persisted togame_briefingviaajax-steps.php. Plumbed into every live AI prompt through a shared helperbuildAudienceFragmentFromBriefing(). -
Briefing form prefills when editingLoading
index.php?gameUniqueId=XXXnow hydrates$gameDatafromgame_briefing, so all fields (including the new Audience section) show the stored values. The save path already handled updates, so edits round-trip cleanly. -
My Games rebuilt as a manage-games table
index-games.phpnow uses DataTables (sortable / searchable / paginated) and shows pitch, kind (frombuildGameFramingFromBriefing), status badge, and dates. Action column links to all four stages: Briefing / Pieces / Decks / Cards. Closes the "write-only briefing" problem end-to-end. -
v1 deck-builder types catalog migrated
cardtypes-default.jsonsplit intoassets/data/types/deckbuild.json(6 roots) andassets/data/subtypes/deckbuild.json(18 subtypes). Dropped location / role / trap(type) / rulemod, renamed resource → resource_card, promoted trap to an event subtype. Widened Unit's and Event'sallowed_behavioursso subtypes no longer leak behaviours outside the parent's list. See the schema doc for the full proposed model.
Quick fixes
-
Drop
FILTER_SANITIZE_STRINGUsepreg_replace('/[^a-zA-Z0-9]/','',$uniqueId)for the ID,htmlspecialchars()on output for user strings. -
Validate
player_count_min ≤ player_count_maxBoth client (min/maxattrs linked) and server side. -
Delete dead JS from this pageRoughly lines 879–1193:
.use-this-resource,showStep(), card-family toggles, commented-out resources submit. Keep only the IA suggestions handler, completeness meter, mechanic/subgenre cascade, and the main submit flow. -
Remove unused imports on this pageDataTables, SmartWizard init, noUiSlider CSS override, unused
$genresload. -
Drop the unconditional
sleep(5)in the init handlerEither show real progress or navigate immediately when there's no AI call.
Bigger changes aligned with the goal
-
Add a “Physical print” section to the briefing review laterCard size preset (poker/bridge/tarot), expected copies, language(s), box format. Deferred: these specs mostly matter at export/layout time, so revisit when the print-output pipeline exists.
-
“Briefing review” step before Step 2After save, show the AI's restatement and ask "does this match?". First concrete moment where the product feels like an assistant.
-
Remove or relabel “Digital-Only” product modelContradicts the print-first product. Rename to "Print + digital companion" or remove.
-
Populate subgenres for non-deck-builder mechanicsOnly Deck-Builder currently has subgenres in
game-taxonomy.json. TCG, Draft, Set Collection, Trick-Taking all need their own lists.
Open questions for the product
- Is Step 1 meant to be re-openable after creation, or intentionally one-shot? (If re-openable, bug-2 becomes higher priority.)
- Should the mechanic choice restrict downstream options (card types, zones), or just influence defaults?
- Is team play really out of scope for the MVP? The field is collected, hidden, disabled — pick one.
- Which AI provider/model is used? Shapes how we template prompts and manage the message history.