GameBuilder and Gamify workspace roadmap

Discovery and proposed implementation sequence, 2026-10-05. No game files have been moved. P1’s single-level save/load/recovery slice is implemented; the remaining migration milestones are still proposals.

Goal and confirmed scope

Bring Gamify into the GameBuilder system as a reference game students can load, play, inspect, modify, save, and eventually author through the panels. Keep ordinary GameEngine JavaScript visible: the workspace is a teaching tool, not a replacement for learning code.

Confirmed decisions:

This is a discovery roadmap, not a gated, executable migration plan. A formal feature specification and reviewed design artifacts have not been supplied. Freeze the document, persistence, and module-loading contracts before turning these milestones into a final implementation task set.

What the current code tells us

Save is incomplete at the workspace boundary

BaseRunner restores stored editor text and its Save button writes that text through StorageManager. The key comes from the page permalink and runner ID in game.html.

app.mjs independently creates a default builder document on every load. It preserves existing runner code at initialization and asks before replacing differing code through Push. The initial AST-based Pull reads one supported GameBuilder-style level without executing or changing source; custom behavior remains code-owned and blocks destructive Push.

Therefore the current source does not establish an unconditional startup overwrite bug. It does establish a mismatch: saved code returns, but the matching panel settings do not. Generating from the reset panel can then replace that saved code after confirmation. Browser reproduction should verify the reported symptom before changing the shared runner.

Saving only a runner entry module also does not save changes to imported level files. A complete game save must include the editable module sources.

Runner save-state contract

Proposed event: ocs:runner-saved, dispatched from the runner container and bubbling within the page. Keep the event generic so other CodeRunner consumers can reuse it, while GameBuilder filters by its own runner identity.

Proposed versioned detail:

{
  schemaVersion: 1,
  runnerId: "gamebuilder-v2",
  storageKey: "_gamebuilder_v2__gamebuilder-v2",
  source: "<exact source successfully persisted>",
  revision: "<unique save revision>"
}

The storage key above is illustrative; use the actual runner key, not a hardcoded value. The event describes a source save. Game identity, selected module, and builder configuration belong to the workspace, not the generic runner. Capture the game/module association at save initiation, especially if workspace persistence later becomes asynchronous.

Acceptance criteria: one successful save produces one event containing the exact persisted source; a failed storage write produces no saved event and visible failure feedback. Another runner’s save is ignored by GameBuilder. Editing after a save marks source dirty; returning to saved text clears that source flag without falsely clearing pending panel changes. Reload restores the saved snapshot without regeneration, and a late subscriber can query it. An integrated workspace-save failure never shows complete-workspace success.

Gamify is a useful compatibility target, not just a simple fixture

The current source has four entry levels, another nested level, a standalone minigame, and 41 image files:

Source Existing behavior to retain Initial workspace treatment
GameLevelWater.js Gravity Player, custom sprite directions, NPC portal, Shark, audio Source-editable; gravity and portal panels later
GameLevelDesert.js Many NPCs, Coin, Clicker, dialogue, AI interactions, nested games and links Source-editable; preserve callbacks and dependencies
GameLevelEnd.js Two Players, parallax and regular backgrounds, Enemy, Collectible, counters and timer Source-editable; do not force into one Player/background
GameLevelOverworld.js Background, Player, Villager, Creeper, custom update behavior and embedded platformer Source-editable; preserve custom lifecycle code
GameLevelStarWars.js Player, turret NPC, two Projectiles, rotations and translation settings Small source-editing pilot, but not fully panel-supported
PlatformerMini.js Custom canvas, gravity, platforms, input, dialogue and exit lifecycle Preserve as a custom module, not a generated GameEngine level

The sources are currently under gamify/. Imports also reach shared GameEngine modules such as MeteorBlaster, Coin, Shark, Creeper, Projectile, and AI/dialogue helpers. Shared audio and referenced external pages must be inventoried as dependencies, not assumed to be owned Gamify images.

The builder currently supports one background, one Player, NPC greetings, and spline barriers. Its catalog has only two movement presets; its generator chooses frame ranges and default timing. That cannot faithfully regenerate all these levels. In particular, a 3-row mirrored sprite is not equivalent to a 4-row movement preset.

Proposed ownership and layout

Use GameBuilder as the registered system and Gamify as a bundled example game:

_projects/systems/gamebuilder/
├── index.md                       # Workbench, existing entry point
├── notebooks/
│   └── gamify.ipynb                # Moved play/teaching page; keep /gamify
├── games/
│   └── gamify/
│       ├── game.json              # Identity, entry, ordered levels, dependencies
│       ├── assets.json            # Asset metadata and stable IDs
│       └── levels/                # Preserved ordinary JavaScript sources
├── images/
│   ├── bg/gamify/                 # Background images
│   ├── sprites/gamify/            # Characters and spritesheets
│   └── objects/gamify/            # Items, projectiles, UI art, platform textures
├── js/                            # Existing modules plus focused workspace modules
├── scripts/
│   └── build-game-catalog.mjs      # Proposed deterministic build-time catalog
├── docs/
│   └── gamify/                    # Moved reference-game documentation
└── tests/

This layout is proposed, not created. Keep nested games as content of the registered GameBuilder project, not as deeper independently registered projects: the current template assumes flat or category/project depth.

Before moving:

Workspace document and source ownership

Design a versioned game document above the current single-level builder state. The reviewed contract should cover:

Do not treat a saved document as a running-game checkpoint: runtime scores, positions, timers, and progress are a separate feature.

Two deliberate authoring modes prevent destructive conversion:

  1. Builder-owned: panels generate ordinary JavaScript. Manual edits remain saved and runnable; generating again requires explicit replacement consent.
  2. Code-owned: edit and save source through the existing runner editor. The panel can show metadata and unsupported-feature notices, but must not generate a simplified replacement.

Panel import comes later. Parse supported syntax with an AST, never execute constructors or callbacks to discover data. Preview recognized fields and unsupported expressions. Preserve original source; block conversion if it would lose behavior. Do not promise arbitrary JavaScript round trips.

Prioritized milestones and work breakdown

P1. Complete save/load for the current builder

Current status: the single-level slice is implemented: automatic draft recovery, a separate explicit save/load return point, JSON import/export, exact source export, and awaited runner Save integration. Unfinished edits are kept. Named Save As libraries and multi-module packages remain future work. See the workspace usage guide.

Done when: create a game with two NPCs and a hidden spline barrier, manually edit its source, save, reload, and recover both the exact source and all panel settings. Export/import restores the same document. Failed saves do not show success. Ordinary lesson Save Code still works.

P2. Load, edit, and save multi-module games

Done when: change a greeting inside one imported level, save/reload, and play the saved change. A second level and a supporting module retain their independent edits. Export/import preserves all module source text.

P3. Build the catalog and move Gamify without rewriting behavior

Done when: identical inputs produce identical JSON; an invalid dependency fails the build visibly. All original entry levels and nested games remain accessible. Existing /gamify links and needed legacy imports still resolve.

P4. Add assets and selectively enable panel editing

Done when: adding an asset through metadata makes it selectable after a development rebuild; renaming its display label does not break a save. A panel edit cannot remove a code-owned component.

P5. Game-in-Game controls

Done when: enter and exit a child repeatedly without duplicated controls or losing the parent level; Stop while nested cleans up both games.

P6. Gravity controls

Done when: enabled and disabled gravity produce the existing documented Player behavior, survive save/load, and do not alter ordinary movement.

Build and verification approach

Use the repository Makefile workflow, not an alternate publishing pipeline:

Migration comparisons must distinguish pre-existing level defects from regressions. For example, Water currently uses an older nested-game construction pattern, while Desert uses parentControl. Do not silently modernize both as part of a directory move. Record failures and handle tightly related fixes in the feature milestone with explicit behavior checks.

Requirement coverage

Requirement Milestones Completion evidence
Load, edit, save complete games P1, P2 Exact panel/source restoration and multi-module saves
Emit and expose successful runner save state P1, P4 Exact saved-source event, queryable snapshot, failure and import safeguards
Preserve handwritten code and existing behavior P1-P5 Code-owned mode, source preservation, migration comparisons
Bring Gamify and GameBuilder together P3 One registered system, bundled reference game, retained links
Move assets into builder-compatible ownership P3, P4 Audited path map, stable metadata, resolving asset references
Generate needed JSON through make P3 Deterministic catalog and visible build failures
Load and modify individual levels P2, P4 Independent buffers, saved-source execution, bounded panel import
Add assets P4 Documented metadata workflow and watch rebuild
Support Game-in-Game authoring P5 Generated actions and parent/child lifecycle checks
Support gravity authoring P6 Existing engine flag preserved and verified

First implementation slice

The first P1 slice now saves/reopens the current builder game, including manual source edits, without moving Gamify. Next expand to P2 so moving Gamify produces genuinely editable saved games rather than a catalog of imports into read-only published modules.