Plans  ›  Step 2 rebuild

Step 2 rebuild — Skeleton sections

Concrete plan for restructuring index-step2.php to host the four Skeleton-phase concepts (trackers, zones, win conditions, effects vocabulary) as live cross-referenced sections. Replaces today's Resources / Life / Zones blocks with a unified, recipe-driven editor that updates reactively as the schema grows.

Implements Roadmap Phase 3 steps 3.1, 3.2, 3.3 + a new effects-vocabulary step Foundation: ~3–5 days · Sections: ~2–4 days each

Implementation status

What's shipped vs pending in this plan.

StepSubjectStatus
F.0a Schema store (js/step2-schema-store.js) + save endpoint (ajax-step2-save.php) shipped
F.0b Step 2 page restructured with the Skeleton block + collapsible sections + status pill scaffolding shipped
F.0c Schema validator (js/step2-schema-validator.js) + status pill driver shipped
F.0d Legacy migration on read dropped — no active games exist; not needed
S.1 Trackers section with 7 recipes (js/step2-section-trackers.js). Legacy Resources card hidden. shipped
S.2 Zones section with 10 recipes (js/step2-section-zones.js). Legacy Game Zones card hidden. shipped
S.3 Effects vocabulary multi-select with smart-disabled prerequisites. Saves to game_meta['enabled_mechanics'] (back-compat with existing AI-prompt code). Legacy "Enabled Mechanics" card hidden. shipped
S.4 Win Conditions section with 6 recipes (combat, race_to_n, most_at_end, multi_source_score, weighted_score, last_standing) shipped
P.1 win_shape dropdown on Step 1 briefing dropped — the cross-reference architecture made the Step 1 dropdown unnecessary; recipe picker on Step 2 is self-sufficient. Briefing's free-text game_goal remains as the human label and feeds into AI autosuggest.
What works on Step 2 right now

A user can author trackers (life, resources, score, status, achievements) and zones (decks, hands, market, tableau) using recipe-based forms. All edits autosave; the validator surfaces errors and warnings as red/yellow badges; the status pill at the bottom summarises the schema state. The legacy Resources / Game Zones cards are hidden but their PHP scaffolding is left in place pending a downstream readers update (Steps 3/4/5 still read the legacy keys; trackers-only games will see empty resource state on those pages until those readers are migrated).

Forward-reference fields

Several zone fields point at concepts authored in later phases — constraints.accepts_types (Card Types), auto_refill.trigger / reset.trigger (Phases), setup.start_filled_from (Decks). They're left in the form deliberately. See Appendix C.9 for the field-by-field map.

Why this needs its own plan

The roadmap's Phase 3 lists trackers (3.2), zones (3.3), and win conditions (3.1) as separate steps. They are — but they share UI surface (Step 2), share a client-side architecture (the live schema store), and share a migration concern (legacy game_parts / game_meta['life_points'] reads). Building them one at a time without a unified plan means redoing the foundation three times.

This plan separates foundation (must ship first, locks the architecture) from sections (can ship in any order once the foundation is in place). The foundation is small and contained; the sections are the real feature work.

Architecture recap (live cross-references)

The reframing

Step 2 is the game's live schema, not a sequential checklist. Sections cross-reference by id: adding a tracker makes it appear in every dropdown that lists trackers. The dependency order (trackers→zones→win conditions→effects) is suggestion, not constraint — users iterate in any order they want. See the conversation that landed this framing: For a game designer, plus the appendices A / B / C.

Three architectural commitments

  1. One client-side store (window.gameSchema) holds the live shape. Sections render from it; saves write to it; cross-section dropdowns read from it.
  2. Auto-save per section (debounced) replaces the "Save & Continue" button as the persistence mechanism. Continue becomes a navigation button only.
  3. Live validation, non-blocking. Broken cross-references (a win condition naming a deleted tracker) get red badges; user can keep editing. The "schema OK" status pill at the bottom of the page is the new "you're ready" signal.

Visual layout of the rebuilt Step 2

┌─ STEP 2 — Skeleton ──────────────────────────────────────┐ │ │ │ ─── SKELETON (Phase B in the design model) ─── │ │ │ │ 1. Trackers [autosave] │ │ (replaces Resources + Life) │ │ │ │ 2. Zones [autosave] │ │ (replaces today's free-text zones) │ │ │ │ 3. Win Conditions [autosave] │ │ (uses win_shape from Step 1 + trackers + zones) │ │ │ │ 4. Effects vocabulary [autosave] │ │ (multi-select; gated by trackers/zones present) │ │ │ │ ─── ANATOMY (will move to Step 2.5 later) ─── │ │ │ │ 5. Card Types │ │ 6. Card Families │ │ │ │ ─── status ───────────────────────────────────────── │ │ ✓ Schema valid · 4 trackers · 5 zones · 1 win cond. │ │ │ │ [ Continue → Step 3: Decks ] │ └──────────────────────────────────────────────────────────┘

Foundation (must come first)

Goal
Lock the architecture before any section work. Without this, every section reinvents save / load / cross-reference / validation.
Estimate
~3–5 days, focused.
Deliverable
Step 2 page renders with empty sections; the store + autosave + validator + migration all work end-to-end against a stub section.

Foundation steps

  • F.0a — Client-side schema store + autosave helper shipped
    What: A small JS module (js/step2-schema-store.js) that holds the live gameSchema object, exposes saveSection(name, data) with debounced AJAX, and emits change events for reactive dropdowns.
    API:
    • SchemaStore.init(initialSchema)
    • SchemaStore.get() — returns the live shape
    • SchemaStore.update(section, data) — updates + triggers save
    • SchemaStore.on(section, callback) — subscribe to changes
    • SchemaStore.findReferencesTo(type, id) — returns sites referencing a given id
    Files: js/step2-schema-store.js (new), ajax-step2-save.php (new endpoint that accepts {section, data}, persists to game_meta).
    Acceptance: with a stub section that writes {foo:"bar"}, hitting save in the browser persists to the DB; reload shows the value back; the store fires change events to subscribed dropdowns.
  • F.0b — Step 2 page restructure (skeleton + collapsibles) shipped
    What: Replace the existing Step 2 layout with the four-section Skeleton hub above the existing Card Types / Families. Each section is a collapsible <details> block with a stub body initially. Auto-save indicator + status pill at the bottom.
    Files: index-step2.php (substantial rewrite of the form section, leaving Card Types / Families untouched).
    Approach: keep all existing PHP state-loading logic intact; rebuild only the form HTML for the new sections. Old sections stay where they are.
    Acceptance: page loads with four empty Skeleton sections + the existing Card Types / Families; existing data renders unchanged; the four stub sections are wired up to the store from F.0a.
  • F.0c — Schema validator + reference finder shipped
    What: Two pieces, both in the same JS module:
    • SchemaValidator.validate(schema) — returns a list of {section, severity, message} issues. Severities: error (red badge) | warning (yellow). Non-blocking.
    • SchemaStore.findReferencesTo(type, id) — walks the schema and returns places that reference a given tracker / zone / etc id. Used by delete confirmations.
    Files: js/step2-schema-validator.js (new).
    Acceptance: after each save, validator runs and section badges update; deleting a referenced tracker shows a confirmation modal listing the referencing sites.
  • F.0d — Legacy data migration on read dropped
    What: When the page loads and game_meta['trackers'] is missing, build it on the fly from existing game_parts(part_type='resource') rows plus game_meta['life_points']. Same pattern for zones (legacy game_meta['game_zones'] blob → structured shape).
    Files: a helper php-includes/inc-step2-migrate.php with two functions: migrateLegacyTrackers($conn, $gameId): array and migrateLegacyZones($conn, $gameId): array.
    Approach: read-only migration. Don't write the new shape until the user actually saves a section through the new UI. That way existing games keep their legacy storage until they're touched, and we have a clear "user has migrated" signal.
    Acceptance: a game created in the old UI loads correctly in the new UI with its resources / life / zones visible as trackers / zones; no DB writes happen on page load.

Sections (any order after foundation)

Once the foundation is shipped, the four Skeleton sections can be built independently. Each replaces a stub from F.0b with a real recipe-based form. They don't depend on each other for implementation — only at runtime through live cross-references, which the store handles.

Suggested order is by user value: trackers first (replaces a real bug today — Resources + Life duality), zones second (same recipe pattern, validates it generalises), effects third (small multi-select, no real complexity), win conditions last (depends on the JSON Logic evaluator from Roadmap step 3.1a, which can be built in parallel).

Section build steps

  • S.1 — Trackers section (Roadmap 3.2) shipped
    Read this first: Appendix B — Trackers (especially B.6 for UI / recipes).
    What: Replace the stub Trackers section with: list view (rows showing icon + label + type/role badges + scope + edit/delete) + "+ Add tracker" button opening the recipe picker.
    Recipes to ship: the seven from B.6 (life_points, generic_resource, victory_points, action_points, player_status, achievement_set, shared_threat) plus a "Custom" option for the full form.
    v1 scope: the four types (counter / boolean / enum / set) and six roles. Defer derived trackers and per-card counters to v1.5 (Roadmap parking).
    Acceptance: user can add a tracker via a recipe in 3 clicks, see it in the list, edit it inline, delete it (with reference-finder warning if it's referenced elsewhere). Saved to game_meta['trackers'] via the store.
  • S.2 — Zones section (Roadmap 3.3) shipped
    Read this first: Appendix C — Zones (especially C.5 for UI / recipes).
    What: Same pattern as trackers: list + recipe picker + form. The 10 recipes from C.5 (personal_deck, hand, in_play, discard, shared_market, shared_deck, shared_pool, current_trick, won_tricks, tableau).
    v1 scope: the four scopes / five visibilities / five orderings, the three auto_refill.when_empty policies. Defer multi-zone selectors, hand-passing, per-card ownership / visibility in shared zones (all C.6 stretches) to v1.5.
    Acceptance: user can add a zone via a recipe in 3 clicks. Saved to game_meta['game_zones'] in the new structured shape (legacy reads still work via F.0d migration).
  • S.3 — Effects vocabulary section shipped
    What: Multi-select checklist of effects from assets/data/effects.json, grouped by mechanic family (deal_damage, gain_resource, draw_cards, etc). Each effect's checkbox is enabled only if the game has the trackers / zones it requires. Greyed-out effects show a tooltip explaining why ("requires a tracker with role: life").
    Files: renders from $_game_defaults['effects']; saves to game_meta['enabled_effects'] as an array of effect_key values.
    Smart filtering: a small inference step that reads each effect's params and checks: do we have a tracker with the right role? a zone of the right scope? Disable the checkbox if not.
    Acceptance: user can toggle effects on / off; disabled effects show why; the AI card-generation prompt later reads only the enabled set.
  • S.4 — Win Conditions section (Roadmap 3.1) shipped
    Prerequisite: Roadmap step 3.1a (the JSON Logic evaluator, inc-game-expressions.php) is already shipped. Can be built in parallel with foundation.
    Read this first: Appendix A — Win conditions (A.0 architectural principle, A.5 recipes, A.7 worked examples).
    What: Reads win_shape from Step 1, renders the shape-specific recipe form. Form dropdowns (tracker pickers, zone pickers) read live from the store — adding a tracker upstream immediately makes it selectable here.
    v1 recipes: the six from A.9 v1 (combat, race_to_n, most_at_end, multi_source_score, weighted_score, last_standing). Defer cooperative / boss / team / hidden to later.
    Output: JSON Logic stored in game_meta['ending'] per A.1 schema.
    Natural-language preview: the form shows what the structured ending says in human terms ("Game ends when any player's Authority reaches 0. That player loses.") so the designer can sanity-check without reading JSON.
    Acceptance: picking a recipe and filling its form generates a valid JSON Logic expression that the evaluator from 3.1a returns the expected boolean for, against a stub Game state.

Step 1 changes (parallel, small)

  • P.1 — Add win_shape dropdown to Step 1 briefing dropped
    What: A single dropdown on the Step 1 form: "What kind of victory?" with the six recipe ids as options (combat, race_to_n, most_at_end, multi_source_score, weighted_score, last_standing). Free-text game_goal stays as the human-readable label alongside.
    Files: index.php (form), ajax-steps.php (save handler), game_meta['game_briefing'] shape.
    Why: Step 2's Win Conditions section (S.4) reads win_shape to know which recipe form to render.
    Acceptance: the dropdown saves; Step 2's Win Conditions section reads it correctly.
    When to ship: any time before S.4. Could land in the same PR as F.0b or as its own tiny PR.

Definition of done

The Step 2 rebuild is complete when:

  1. A user can land on Step 2 with a fresh game and author trackers, zones, win conditions, and effects via recipe-based forms, in any order they choose.
  2. Existing games (legacy storage) load correctly without manual migration.
  3. The schema validator runs after each save; broken cross-references show inline; the bottom-of-page status pill reflects "schema valid" or "N issues".
  4. Card Types and Families still work as before, visually separated from the new Skeleton block.
  5. The simulator (when it exists, Roadmap 3.8+) can read game_meta['trackers'], ['game_zones'], ['ending'], ['enabled_effects'] and build a runnable Game state from them.
The win signal

Open Step 2, add a tracker via the life_points recipe, add a zone via the personal_deck recipe, scroll to Win Conditions, pick the combat recipe, and watch the tracker dropdown have life_points ready to pick — without any page reload between steps. That's the architecture working.

Mapping to the high-level roadmap

This plan is the implementation detail behind several Roadmap entries. The roadmap stays high-level; this plan is where the substeps live.

Roadmap stepThis plan covers
3.1 — Structure win conditions P.1 (Step 1 dropdown) + S.4 (Win Conditions section). Roadmap step 3.1a (the evaluator) is a prerequisite, not part of this plan.
3.2 — Unify trackers S.1 (Trackers section).
3.3 — Promote zones S.2 (Zones section).
(new — not in roadmap yet) S.3 (Effects vocabulary section). Worth adding to the roadmap as 3.3.5 or absorbing into 3.3.
(implicit, foundational) F.0a – F.0d (architecture). One-time cost; pays back across all four sections.

The foundation work doesn't appear in the roadmap because it's architecture-not-feature. If you want it visible in the roadmap, add a 3.0 step ("Step 2 architecture: live schema store + autosave + validator") that links here.

Phase B in the design model

This entire plan implements Phase B (Skeleton) from the designer order: the user-facing surface for trackers, zones, win conditions, and effects. Phase A (concept / briefing) is already shipped on Step 1; Phase C (anatomy: phases / actions / setup) and Phase D (content) come later. When this plan is done, the wizard can capture a complete game definition up to and including the skeleton — one short step away from being simulator-ready.