# Project export: sprite2world

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: rom sprites to worlds in one workflow. AI understands your assets, builds 2D levels, lets you iterate instantly, and exports them for your game.
- Devpost: https://devpost.com/software/sprite2world
- GitHub: https://github.com/RichardsWelt/sprite2world
- Video: https://www.youtube.com/embed/artZwcZ3V-0?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Richard Magney (1 commits)

## Devpost submission (written by the team)

### Overview

The AI designs. Sprite2World engineers. Sprite2World is a containerized, AI-assisted level-design tool that turns a collection of pixel-art sprites and a natural-language idea into a deterministic, validated, playable 2D world. Users can import PNG files or ZIP archives, classify assets, describe the world they want, generate a semantic blueprint with OpenAI, and immediately inspect and playtest the resulting map in the browser.

### Inspiration

The project started with a recurring problem in 2D game development: having a folder full of promising sprites is not the same as having a playable level. Turning those assets into a coherent world still requires categorization, layout planning, collision rules, connectivity checks, iteration, and a great deal of manual placement. Generative AI is good at interpreting creative intent, but asking a model to output thousands of exact tile coordinates is fragile and difficult to reproduce. We wanted a clearer division of responsibility: let AI understand the visual assets and propose the structure of a world, while conventional software engineering remains responsible for coordinates, rules, validation, and playability. That idea became the central principle of Sprite2World: AI expresses intent; deterministic code builds the world.

### What it does

Sprite2World provides an end-to-end workflow for creating a playable top-down level: Import pre-sliced PNG sprites, multiple files, folders, or ZIP archives. Review the adaptive sprite library and assign semantic roles manually or with OpenAI vision classification. Describe the desired world in natural language. Generate a schema-constrained semantic blueprint containing regions, connections, themes, and gameplay intent. Convert that blueprint into rooms, corridors, walls, objects, collision cells, a player start, and an exit. Validate reachability, connectivity, overlaps, boundaries, collisions, and asset references. Repair bounded obstacle problems automatically and display remaining diagnostics. Playtest the result immediately with keyboard controls. Improve the world through natural-language feedback, restore earlier versions, or export the full project as JSON and a PNG preview. The editor also includes a deterministic offline demo workflow. This means the core product remains explorable even when no OpenAI API key is configured or an AI request is unavailable.

### How we built it

Sprite2World is completly built with Codex (ChatGPT 5.6 Sol) C# and .NET 10 as a Blazor Web App. Docker Compose starts two services: sprite2world-web provides the browser editor, project state, OpenAI orchestration, and Canvas integration. sprite2world-worker performs bounded file processing, deterministic world generation, validation, repair, and preview rendering. Only the web service is exposed to the host. The worker stays on an internal Docker network, and both services share a named volume for JSON project files and imported assets. This keeps installation simple: users only need Docker and a browser—no local .NET SDK, Node.js installation, database, or message broker. The OpenAI Responses API is used for asset classification, blueprint creation, and feedback-driven revision. AI responses use strict Structured Outputs and are deserialized into an OpenAI-independent semantic model. The model never supplies final tile coordinates. The concrete map is produced by a seeded TopDownRooms generator. Conceptually, the result is a pure function of the validated inputs: $$ W = f(B, A, S, V) $$ where $W$ is the generated world, $B$ is the semantic blueprint, $A$ is the classified asset manifest, $S$ is the seed, and $V$ is the generator version. Keeping these inputs constant produces the same world again. An independent validator then checks the generated result using grid traversal and flood-fill logic. It verifies that the exit is reachable, required regions are connected, rooms do not overlap incorrectly, collision data is valid, and every referenced asset exists. A bounded repair step can remove blocking obstacles without silently redesigning the level. The browser renders the world through a lightweight JavaScript Canvas layer with pan, zoom, grid, minimap, camera following, and keyboard playtesting. Project data is stored as JSON, while a dependency-free PNG encoder creates diagnostic previews. Challenges we faced Creating a reliable boundary between AI and game logic The hardest design decision was deciding what the model should control. Giving AI complete control over tile placement would have made results inconsistent and difficult to validate. We instead designed a compact semantic blueprint schema and moved all spatial engineering into deterministic C# code. Making generated worlds genuinely playable A visually plausible map can still contain an unreachable exit, disconnected regions, invalid collisions, or blocked corridors. We built validation as an independent stage rather than trusting the generator. This separation made failures explainable and allowed safe, bounded repairs. Handling arbitrary user assets safely File import introduced its own security and reliability problems. The importer validates extensions, sizes, PNG signatures, dimensions, normalized paths, duplicate entries, and extraction limits. ZIP entries are checked against path traversal before anything is written. Preserving determinism while supporting iteration Creative feedback should change the design without turning the system into an unpredictable black box. We therefore version semantic blueprints and preserve seeds and generator metadata, allowing users to understand what changed and restore earlier results. Delivering a zero-setup experience The project spans browser UI, server-side AI calls, file persistence, and CPU-oriented generation. Packaging everything into two healthy Docker services—while correctly publishing Blazor's interactive browser assets—was an important final challenge. The result can now be cloned and started with a single Docker Compose command.

### What we learned

We learned that AI becomes more useful when its responsibilities are deliberately constrained. Strict schemas do not reduce creativity; they create a dependable contract between creative intent and deterministic systems. We also learned that generation and validation should be separate concerns. A generator tries to produce a good result, while a validator assumes nothing and proves whether the result satisfies the rules. That distinction is especially valuable for AI-assisted workflows. Finally, we learned how important graceful degradation is. By including deterministic demo content and an offline fallback, Sprite2World remains testable even without external services. This made the product easier to demonstrate, debug, and trust. What we are proud of Sprite2World does more than create a visually interesting image. It produces a structured world with rooms, connections, collisions, a start position, an exit, validation results, version history, and exportable data. Users can immediately play what they generated and inspect the reasoning boundary between AI design and deterministic engineering.

### What's next

The current release intentionally focuses on one reliable grammar: TopDownRooms. Future versions could add overworld, city, platformer, and multi-floor generators; sprite-sheet slicing and animation; richer editing tools; collaborative projects; and exporters for engines such as Godot, Unity, and Tiled. The long-term goal is to make Sprite2World a transparent bridge between creative assets, natural-language direction, and production-ready level data.

## README (from the GitHub repository)

# Sprite2World

**The AI designs. Sprite2World engineers.**

Sprite2World is a containerized AI-assisted level-design tool for importing pre-sliced PNG sprites, creating a semantic room blueprint with OpenAI, engineering it into a deterministic tile map, validating and repairing that map, and playtesting the result immediately in the browser.

## How Codex & GPT-5.6 were used

This project was built end-to-end with **Codex as the primary engineering collaborator** and uses the **GPT-5.6 model family as the creative intelligence inside the product**. Under human direction, Codex supported nearly every layer of the build—from product framing and architecture to implementation, debugging, browser QA, Docker packaging and submission documentation.

### Codex during development

Codex was used as an active engineering partner throughout the complete development loop:

- **Product and architecture:** turned the initial concept and visual references into a scoped MVP, a two-service architecture and a clear boundary between AI intent and deterministic game logic.
- **Full-stack implementation:** created and refined the C# domain, application, infrastructure, worker and Blazor web layers; implemented the Canvas editor, persistence, import pipeline, generator, validator, repair flow and exports.
- **UI and interaction design:** translated design feedback into the three-column editor, onboarding, project and sprite libraries, inspectors, dialogs, responsive behavior and accessibility improvements.
- **Debugging and verification:** built containers, inspected logs, tested the application in a real browser and diagnosed release-only problems such as missing Blazor static assets in the optimized Docker build.
- **Quality and release work:** added deterministic tests, security checks for PNG/ZIP imports, health checks, Docker Compose setup, architecture notes and clean-clone verification from the public GitHub repository.

Codex did not merely generate a code sample. It was used iteratively to inspect real results, identify failures, edit the repository, rebuild the system and verify the finished behavior.

### GPT-5.6 inside Sprite2World

Sprite2World integrates GPT-5.6 through the **OpenAI Responses API** for three focused workflows:

| Workflow | What GPT-5.6 contributes | What deterministic code retains |
| --- | --- | --- |
| Sprite classification | Interprets imported sprite images and assigns semantic roles using vision input | Stable asset IDs, manual overrides, validation and persistence |
| World design | Converts a natural-language request into a strict, schema-constrained semantic blueprint | Room coordinates, corridors, walls, collisions, start/exit placement and seeded generation |
| Feedback iteration | Revises the semantic blueprint from contextual user feedback | Version history, regeneration, validation, repair and export |

The model is deliberately **not** asked to generate thousands of raw tile coordinates. GPT-5.6 handles visual understanding, intent and semantic structure; Sprite2World turns that structure into a reproducible and testable world. This makes the AI contribution both visible and trustworthy.

The model picker exposes the GPT-5.6 family by workload:

- **GPT-5.6 Luna** is the efficient default for cost-sensitive experimentation.
- **GPT-5.6 Terra** offers a balance of capability and cost.
- **GPT-5.6 Sol** provides the highest-quality option for demanding world-design prompts.
- The **GPT-5.6** alias routes to Sol.

Users can also choose low, medium or high reasoning effort. API responses use strict Structured Outputs and `store=false`, and an unavailable AI request falls back to an explicit deterministic demo blueprint so the core workflow remains testable.

This division of labor is the project's central idea:

> **GPT-5.6 designs the intent. Sprite2World engineers the playable result.**

Learn more in OpenAI's [GPT-5.6 model guide](https://developers.openai.com/api/docs/guides/model-guidance?model=gpt-5.6).



## MVP capabilities

- polished Blazor Web App editor with a three-column, desktop-first dark UI
- ZIP, multi-PNG and browser-supported folder import with PNG validation, stable content IDs and ZIP-slip protection
- adaptive asset library with search, role filter, exclusion, folder hints, manual overrides and OpenAI vision classification
- OpenAI Responses API with strict Structured Outputs for classification, blueprint creation and feedback revision
- deterministic `TopDownRooms` generator with rooms, orthogonal corridors, loops, walls, collisions, objects, start and exit
- independent pathfinding/validation and bounded obstacle repair
- Canvas rendering, pan/zoom/grid, minimap and keyboard playtest (WASD or arrow keys)
- version history and contextual feedback iteration
- complete project JSON and PNG preview export
- JSON-file persistence in a named Docker volume; no database
- a licensed seven-sprite starter pack plus procedural demo tiles for a sub-three-minute walkthrough

The first release intentionally supports one reliable grammar, `TopDownRooms`. Overworld, city, platformer, auto-tiling, animation and engine-specific exports are extension points, not claimed features.

## Screenshots

The application opens directly into the deterministic demo workspace. Add current screenshots from `http://localhost:3000` here when preparing the hackathon submission.

## Quick start with Docker

The only prerequisite is a running Docker Desktop installation or Docker Engine with Docker Compose. .NET, Node.js, a database and an OpenAI key are **not** required to start the application.

```bash
git clone https://github.com/RichardsWelt/sprite2world.git
cd sprite2world
docker compose up -d --build --wait
```

Open [http://localhost:3000](http://localhost:3000). On the first visit, onboarding asks for language and an optional personal OpenAI API key. The editor also starts without AI.

The first build downloads the .NET container images and can take a few minutes. Later builds reuse Docker's dependency cache and are considerably faster.

If port 3000 is already in use, choose another one without editing a file:

```bash
SPRITE2WORLD_PORT=3100 docker compose up -d --build --wait
```

Then open `http://localhost:3100`.

Stop without deleting project data:

```bash
docker compose down
```

Deliberately delete all saved projects:

```bash
docker compose down -v
```

## OpenAI configuration

For the shortest and safest setup, enter a personal API key in the first-start onboarding. The key is validated directly with OpenAI and is never committed to the repository, persisted in projects, exported or logged.

Server administrators can alternatively copy `.env.example` to `.env.local` and set `OPENAI_API_KEY` before starting Docker. Both `.env` and `.env.local` are ignored by Git.

Configuration variables:

- `OPENAI_API_KEY` — project API key
- `OPENAI_DEFAULT_MODEL` — defaults to the cost-sensitive `gpt-5.6-luna`; common compatible OpenAI models are selectable from grouped dropdowns in Settings and the Inspector
- `OPENAI_REASONING_EFFORT` — `low`, `medium` or `high`

If an API request fails, the error is presented in user-safe form and the generator uses an explicit deterministic demo blueprint so the workflow remains testable offline.

## Three-minute demo

1. Open the app; the licensed seven-sprite starter pack, procedural demo tiles and three example worlds are preloaded.
2. Import a PNG/ZIP pack or inspect the categorized demo assets.
3. Choose **AI classify** to classify non-overridden assets with image input.
4. Edit the world description and choose **Generate world**.
5. Inspect **Semantic Blueprint** and **Validation**.
6. Open **Playtest**, choose **Start**, focus the canvas, and move with WASD/arrows to the red exit.
7. Enter feedback such as “Needs another loop” and choose **Improve**.
8. Restore a version from the right panel or export JSON/PNG from the action rail.

Manual classifications always win over later AI classific

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 39 recognized source files, 382 KB.
- C# (language) — detected in the code
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- HTML (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (60 of 60)

```
ARCHITECTURE.md
compose.yaml
Directory.Build.props
Directory.Packages.props
GITHUB_UPLOAD_CHECKLIST.md
LICENSE
PUBLISH_AND_TEST.md
README.md
Sprite2World.sln
src/Sprite2World.Application/BlueprintGraphRepair.cs
src/Sprite2World.Application/BlueprintValidator.cs
src/Sprite2World.Application/DemoBlueprintFactory.cs
src/Sprite2World.Application/DeterministicWorldGenerator.cs
src/Sprite2World.Application/Interfaces.cs
src/Sprite2World.Application/IOpenAiApiKeyProvider.cs
src/Sprite2World.Application/JsonWorldExporter.cs
src/Sprite2World.Application/Sprite2World.Application.csproj
src/Sprite2World.Application/SpriteQualityAnalyzer.cs
src/Sprite2World.Application/WorldRepairService.cs
src/Sprite2World.Application/WorldValidator.cs
src/Sprite2World.Contracts/Contracts.cs
src/Sprite2World.Contracts/Sprite2World.Contracts.csproj
src/Sprite2World.Domain/Models.cs
src/Sprite2World.Domain/Sprite2World.Domain.csproj
src/Sprite2World.Infrastructure/DemoProjectSeeder.cs
src/Sprite2World.Infrastructure/OpenAiDesignService.cs
src/Sprite2World.Infrastructure/OpenAiModelCatalog.cs
src/Sprite2World.Infrastructure/OpenAiOptions.cs
src/Sprite2World.Infrastructure/PngCodec.cs
src/Sprite2World.Infrastructure/PreviewRenderer.cs
src/Sprite2World.Infrastructure/ProjectFileStore.cs
src/Sprite2World.Infrastructure/SafeAssetImporter.cs
src/Sprite2World.Infrastructure/Sprite2World.Infrastructure.csproj
src/Sprite2World.Infrastructure/WorkerClient.cs
src/Sprite2World.Web/appsettings.json
src/Sprite2World.Web/Components/_Imports.razor
src/Sprite2World.Web/Components/App.razor
src/Sprite2World.Web/Components/Layout/MainLayout.razor
src/Sprite2World.Web/Components/OpenAiModelSelect.razor
src/Sprite2World.Web/Components/Pages/Home.razor
src/Sprite2World.Web/Components/Routes.razor
src/Sprite2World.Web/Dockerfile
src/Sprite2World.Web/Program.cs
src/Sprite2World.Web/Services/EditorState.cs
src/Sprite2World.Web/Services/OpenAiCredentialState.cs
src/Sprite2World.Web/Services/UiLocalizer.cs
src/Sprite2World.Web/Sprite2World.Web.csproj
src/Sprite2World.Web/wwwroot/accessibility.js
src/Sprite2World.Web/wwwroot/app.css
src/Sprite2World.Web/wwwroot/libraryStudio.css
src/Sprite2World.Web/wwwroot/projects.css
src/Sprite2World.Web/wwwroot/spriteStudio.js
src/Sprite2World.Web/wwwroot/worldCanvas.js
src/Sprite2World.Worker/appsettings.json
src/Sprite2World.Worker/Dockerfile
src/Sprite2World.Worker/Program.cs
src/Sprite2World.Worker/Sprite2World.Worker.csproj
tests/Sprite2World.Tests/Sprite2World.Tests.csproj
tests/Sprite2World.Tests/WorldEngineTests.cs
THIRD_PARTY_NOTICES.md
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- README edited
- small fixes to avoid HTTP 404
- Adding MapStaticAssets() to avoid HTTP 404
- src and tests upload
- Initial Comit. v.20260719
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### GITHUB_UPLOAD_CHECKLIST.md

```markdown
# GitHub upload package

This folder is the sanitized public source package for Sprite2World.

- No `.env.local`, API key, browser preference, local project data, build output, or Git history is included.
- `.env.example` contains placeholders only. The normal setup needs no env file because users can enter their own OpenAI API key during onboarding.
- On a fresh browser origin, the onboarding dialog opens because browser preferences are not part of this package.
- The licensed starter sprites are included as requested and are loaded into the shared demo library at first start.
- `bin`, `obj`, `TestResults`, audit captures, and operating-system metadata are excluded.
- GitHub Actions performs a secret-free Docker build and fresh-start health check on every push to `main` and on pull requests.

Upload the contents of this folder as the repository root, not the surrounding `GitHub Upload` folder itself.

```

### THIRD_PARTY_NOTICES.md

```markdown
# Third-party notices

Sprite2World's production projects use only the .NET shared framework and no third-party runtime packages.

| Component | Version | License | Project | Purpose |
|---|---:|---|---|---|
| .NET / ASP.NET Core | 10.0 | MIT | https://github.com/dotnet/aspnetcore | Blazor, HTTP hosting, health checks, dependency injection |
| .NET Runtime Libraries | 10.0 | MIT | https://github.com/dotnet/runtime | JSON, ZIP, hashing, compression, IO |
| Microsoft.NET.Test.Sdk | 18.0.1 | MIT | https://github.com/microsoft/vstest | Test discovery/execution (development only) |
| xUnit.net | 2.9.3 | Apache-2.0 | https://github.com/xunit/xunit | Unit tests (development only) |
| xunit.runner.visualstudio | 3.1.5 | Apache-2.0 | https://github.com/xunit/visualstudio.xunit | Test adapter (development only) |

The generated demo sprites are original programmatic assets released under this repository's MIT license. No commercial or unlicensed artwork is included. OpenAI is an optional external API, not a bundled software dependency; API usage may incur separate charges and requires the user's own project key.

```

### src/Sprite2World.Web/Dockerfile

```
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY Directory.Build.props Directory.Packages.props ./
COPY src/Sprite2World.Domain/Sprite2World.Domain.csproj src/Sprite2World.Domain/
COPY src/Sprite2World.Contracts/Sprite2World.Contracts.csproj src/Sprite2World.Contracts/
COPY src/Sprite2World.Application/Sprite2World.Application.csproj src/Sprite2World.Application/
COPY src/Sprite2World.Infrastructure/Sprite2World.Infrastructure.csproj src/Sprite2World.Infrastructure/
COPY src/Sprite2World.Web/Sprite2World.Web.csproj src/Sprite2World.Web/
COPY . .
RUN dotnet restore src/Sprite2World.Web/Sprite2World.Web.csproj
RUN dotnet publish src/Sprite2World.Web/Sprite2World.Web.csproj -c Release -o /app/publish --no-restore
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
USER root
RUN mkdir -p /app/data && chown -R $APP_UID /app
USER $APP_UID
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet","Sprite2World.Web.dll"]

```

### src/Sprite2World.Worker/Dockerfile

```
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY Directory.Build.props Directory.Packages.props ./
COPY src/Sprite2World.Domain/Sprite2World.Domain.csproj src/Sprite2World.Domain/
COPY src/Sprite2World.Contracts/Sprite2World.Contracts.csproj src/Sprite2World.Contracts/
COPY src/Sprite2World.Application/Sprite2World.Application.csproj src/Sprite2World.Application/
COPY src/Sprite2World.Infrastructure/Sprite2World.Infrastructure.csproj src/Sprite2World.Infrastructure/
COPY src/Sprite2World.Worker/Sprite2World.Worker.csproj src/Sprite2World.Worker/
RUN dotnet restore src/Sprite2World.Worker/Sprite2World.Worker.csproj
COPY . .
RUN dotnet publish src/Sprite2World.Worker/Sprite2World.Worker.csproj -c Release -o /app/publish --no-restore
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
USER root
RUN mkdir -p /app/data && chown -R $APP_UID /app
USER $APP_UID
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet","Sprite2World.Worker.dll"]

```

### compose.yaml

```yaml
name: sprite2world
services:
  sprite2world-worker:
    build:
      context: .
      dockerfile: src/Sprite2World.Worker/Dockerfile
    environment:
      ASPNETCORE_ENVIRONMENT: Production
      SPRITE2WORLD_DATA_PATH: /app/data
    volumes:
      - sprite2world-data:/app/data
    networks: [sprite2world-internal]
    healthcheck:
      test: ["CMD", "dotnet", "Sprite2World.Worker.dll", "--healthcheck"]
      interval: 5s
      timeout: 3s
      retries: 12
      start_period: 5s
    restart: unless-stopped
  sprite2world-web:
    build:
      context: .
      dockerfile: src/Sprite2World.Web/Dockerfile
    env_file:
      - path: .env.local
        required: false
    environment:
      ASPNETCORE_ENVIRONMENT: Production
      ASPNETCORE_URLS: http://+:8080
      Worker__BaseUrl: http://sprite2world-worker:8080/
      SPRITE2WORLD_DATA_PATH: /app/data
      OPENAI_DEFAULT_MODEL: ${OPENAI_DEFAULT_MODEL:-gpt-5.6-luna}
      OPENAI_REASONING_EFFORT: ${OPENAI_REASONING_EFFORT:-medium}
    volumes:
      - sprite2world-data:/app/data
    ports:
      - "${SPRITE2WORLD_PORT:-3000}:8080"
    networks: [sprite2world-internal, sprite2world-edge]
    depends_on:
      sprite2world-worker:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "dotnet", "Sprite2World.Web.dll", "--healthcheck"]
      interval: 5s
      timeout: 3s
      retries: 12
      start_period: 10s
    restart: unless-stopped
volumes:
  sprite2world-data:
networks:
  sprite2world-internal:
    internal: true
  sprite2world-edge:

```

### src/Sprite2World.Application/IOpenAiApiKeyProvider.cs

```c#
namespace Sprite2World.Application;

public interface IOpenAiApiKeyProvider
{
    string? ApiKey { get; }
}

```

### src/Sprite2World.Application/JsonWorldExporter.cs

```c#
using System.Text.Json;
using System.Text.Json.Serialization;
using Sprite2World.Domain;

namespace Sprite2World.Application;

public sealed class JsonWorldExporter : IWorldExporter
{
    private static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web) { WriteIndented = true, Converters = { new JsonStringEnumConverter() } };
    public string ExportJson(ProjectExport project) => JsonSerializer.Serialize(project, Options);
}

```

### src/Sprite2World.Infrastructure/OpenAiOptions.cs

```c#
namespace Sprite2World.Infrastructure;

public sealed class OpenAiOptions
{
    public const string SectionName = "OpenAI";
    public string? ApiKey { get; set; }
    public string DefaultModel { get; set; } = OpenAiModelCatalog.DefaultModelId;
    public string ReasoningEffort { get; set; } = "medium";
}

public sealed class StorageOptions
{
    public const string SectionName = "Storage";
    public string DataPath { get; set; } = "/app/data";
    public int MaxAssets { get; set; } = 10_000;
    public long MaxFileBytes { get; set; } = 10 * 1024 * 1024;
    public long MaxTotalBytes { get; set; } = 100 * 1024 * 1024;
}

```

### src/Sprite2World.Infrastructure/OpenAiModelCatalog.cs

```c#
namespace Sprite2World.Infrastructure;

public sealed record OpenAiModelDefinition(string Id, string DisplayName, string Group);

public static class OpenAiModelCatalog
{
    public const string DefaultModelId = "gpt-5.6-luna";

    public static IReadOnlyList<OpenAiModelDefinition> Models { get; } =
    [
        new("gpt-5.6-luna", "GPT-5.6 Luna · günstig (Standard)", "GPT-5.6 · aktuell"),
        new("gpt-5.6-terra", "GPT-5.6 Terra · ausgewogen", "GPT-5.6 · aktuell"),
        new("gpt-5.6-sol", "GPT-5.6 Sol · maximale Qualität", "GPT-5.6 · aktuell"),
        new("gpt-5.6", "GPT-5.6 · Alias für Sol", "GPT-5.6 · aktuell"),
        new("gpt-5.4-nano", "GPT-5.4 nano · günstig", "GPT-5.4"),
        new("gpt-5.4-mini", "GPT-5.4 mini · ausgewogen", "GPT-5.4"),
        new("gpt-5.4", "GPT-5.4 · hohe Qualität", "GPT-5.4"),
        new("gpt-5-nano", "GPT-5 nano", "GPT-5"),
        new("gpt-5-mini", "GPT-5 mini", "GPT-5"),
        new("gpt-4.1-nano", "GPT-4.1 nano", "GPT-4.1 · kompatibel"),
        new("gpt-4.1-mini", "GPT-4.1 mini", "GPT-4.1 · kompatibel"),
        new("gpt-4.1", "GPT-4.1", "GPT-4.1 · kompatibel"),
        new("o4-mini", "o4-mini · schnelles Reasoning", "o-Serie · kompatibel"),
        new("o3", "o3 · starkes Reasoning", "o-Serie · kompatibel")
    ];
}

```

### src/Sprite2World.Application/WorldRepairService.cs

```c#
using Sprite2World.Domain;

namespace Sprite2World.Application;

public sealed class WorldRepairService(IWorldValidator validator) : IWorldRepairService
{
    public WorldGenerationResult Repair(WorldDefinition world, IReadOnlyList<AssetDefinition> assets, int maximumAttempts = 3)
    {
        var validation = validator.Validate(world, assets);
        if (validation.IsValid) return new(world, validation);
        var tiles = world.Tiles.ToList();
        var repairs = world.Repairs.ToList();
        for (var attempt = 0; attempt < Math.Clamp(maximumAttempts, 1, 10) && !validation.IsValid; attempt++)
        {
            var blocked = tiles.Where(t => t.Kind == TileKind.Obstacle).OrderBy(t => Math.Abs(t.X - world.Start.X) + Math.Abs(t.Y - world.Start.Y)).ToList();
            if (blocked.Count == 0) break;
            var remove = blocked[0];
            tiles[tiles.IndexOf(remove)] = remove with { Kind = TileKind.Floor, Walkable = true };
            foreach (var layer in world.Layers.Where(layer => layer.Purpose == LayerPurpose.Decoration)) layer.Placements.RemoveAll(item => item.X == remove.X && item.Y == remove.Y && assets.FirstOrDefault(asset => asset.Id == item.AssetId)?.Role == AssetRole.Obstacle);
            repairs.Add($"Removed blocking obstacle at ({remove.X}, {remove.Y}).");
            world = world with { Tiles = tiles.ToList(), Repairs = repairs.ToList() };
            validation = validator.Validate(world, assets);
        }
        return new(world, validation);
    }
}

```

[29 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]