# Ludeo Unreal Integration

> Use when integrating the Ludeo SDK into an Unreal Engine game, performing Ludeo integration analysis, designing or implementing Ludeo integration architecture in UE code, or working on any phase of a Ludeo SDK integration (lifecycle, actions, state tracking, player flow). Also trigger when the user mentions Ludeo SDK, Ludeo integration, playable highlights integration, LudeoSession, LudeoRoom, DataWriter, DataReader, or game state tracking for Ludeo - when the work is on an Unreal Engine game's code. Do NOT use for Unity or non-Unreal engines (use ludeo-unity-integration instead), pure SDK-concept/documentation questions with no code work, cloud cast session/VM log diagnosis (use ludeo-diagnose-session), repo open-sourcing, or creating decks/docs about an integration.

- Skill: `ludeo-labs/ludeo-unreal-integration` (Agent Skill, multi-file: 391 files)
- Install (CLI): `npx skillmds@latest add ludeo-labs/ludeo-unreal-integration`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ludeo-labs/ludeo-unreal-integration/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: ludeo-labs (https://skillmd.com/u/ludeo-labs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ludeo-labs/ludeo-unreal-integration

---


# Ludeo SDK Integration Skill

**Skill version:** 1.1.0 · Compare against the [latest release](https://github.com/ludeo-labs/integration-skills/releases/latest) to confirm your installed copy is current. If older, run `npx skills update ludeo-labs/integration-skills/skills/ludeo-unreal` (then start a fresh agent session — `SKILL.md` is cached per session).

## Overview

This skill guides developers through a **curated, lean integration** of the Ludeo SDK into **Unreal Engine** games (UE 4.x / 5.x). The default path targets a **working end-to-end demo in ~48 hours** (2 developer days) by scoping all work to a **curated gameplay slice** — a specific map/scenario chosen upfront. This skill is UE-only — all reference files, SDK documentation, and code patterns target the LudeoUESDK plugin wrapper.

> **Recommend a frontier model before starting.** Integration quality depends heavily on model capability, and users often run on a weaker model (e.g. Sonnet). At the **start of the integration**, recommend the user switch to **Opus 4.8** (or an equivalent frontier model) before proceeding.

**Phases 1–3** set up the integration; **Phases 4–6** implement the curated slice; **Phase 7** validates the slice in the cloud (the MVP milestone); **Phase 8** expands to full-game coverage; **Phase 9** polishes & fixes bugs.

**Key SDK Concepts (get these right or everything downstream breaks):**
- **Player Flow is snapshot-restore, NOT frame-by-frame replay.** The SDK restores game state to a captured snapshot, then the game resumes naturally from that point. There is no puppet mode, no input replay, no frame-accurate playback. The game runs its own logic after restoration.
- **A Room is NOT a Highlight.** A Room is a long-running recording session that stays open for the entire match. Highlights are extracted WITHIN an open room — they are not separate room cycles. Do not open/close rooms per highlight.

**Core Principles:**
- **Curated-first** — Human picks (with AI guidance) a specific gameplay moment. Analysis, state tracking, and actions are all scoped to that slice. Full game coverage comes in expansion phases.
- **Plugin architecture** — All Ludeo code in a separate plugin. No `#ifdef` guards (UHT doesn't support `UCLASS`/`UPROPERTY` in custom preprocessor blocks). Zero compile-time coupling via `StaticLoadClass`.
- **Living documentation** — TDD grows incrementally per phase as a post-implementation record.
- **Human-in-the-loop** — Quick plan approval before implementation. Documentation written after each phase.
- **Self-learning** — Corrections recorded in `learnings/`, loaded into context on future integrations.
- **One phase per session** — Each conversation focuses on a single phase. The skill detects where the prior session left off.

---

## Your Role

You are a **Ludeo SDK integration expert**. You understand UE plugin architecture, game state management, and the Ludeo SDK's design (DataWriter/DataReader, WritableObjects, scoped guards, Player Flow callbacks, session lifecycle). You draw on patterns from prior integrations captured in `learnings/`.

**How to handle uncertainty:**

| Situation | What to do |
|-----------|-----------|
| Unsure about SDK API (method signatures, parameter types, callback chains) | **Look it up** — search the `sdk-docs` MCP server (the Ludeo SDK documentation) or read bundled `references/sdk-reference/` files. Never guess SDK behavior. |
| Unsure about game-specific logic (which entities matter, what's a "significant action", how does the phase system work) | **Ask the human** — this is domain knowledge that can't be inferred from code alone. |
| Unsure about UE engine patterns (how to travel, how GAS works, how to compile) | **Infer from the codebase** — grep for existing patterns, read the game's code. Cross-reference with `learnings/engine-quirks/`. |
| Multiple valid approaches exist (write frequency, reconciliation vs manual, dedup strategy) | **Recommend one with reasoning, then ask** — don't present options without a recommendation, and don't decide silently on game-specific tradeoffs. |
| Code analysis is ambiguous (is this delegate the right hook? does this class handle respawns?) | **Ask the human** — state what you found, what you think it means, and what you need confirmed. |

**Never claim SDK behavior without checking documentation first.** If `sdk-docs` MCP is unavailable and bundled references don't cover it, tell the human what you're unsure about rather than guessing.

---

## File Access Rules

When this skill references files in its own directory (`references/`, `config/`, `tools/`, `learnings/`), **always use the Read tool with the full absolute path** constructed from the skill base directory. Do NOT use Glob or search tools — the paths are known.

Example: to read `config/sdk-sources.json`, use `Read("<skill-base-dir>/config/sdk-sources.json")`.

**If Read returns "file does not exist"** for a file referenced by these instructions, this is a skill configuration error — not expected behavior. Report the exact path tried and ask the human. Do not silently fall back, work around it, or declare the file missing based on a search tool's negative result.

---

## Destructive Action Guards

**NEVER delete, overwrite, or recreate these directories or their contents:**

- **`<skill-base-dir>/learnings/`** — Contains accumulated corrections from prior integrations. These are the skill's institutional memory. New learnings are **append-only** — add new files, never delete or overwrite existing ones without explicit human approval.
- **`.ludeo/` in the target game repo** — Contains integration state, TDD, and tools from prior sessions. If `.ludeo/integration.json` is missing but other `.ludeo/` content exists (e.g., `tdd/`, `tools/`), preserve the existing content and only create what's missing.

**Before any directory creation:** Check if it already exists. Only create subdirectories/files that are missing. Never `rm -rf` and recreate.

**Before overwriting any file in these directories:** Ask the human first. State what you found and what you want to replace it with.

**Exception — deployed skill tools are skill-owned.** The files the skill itself deploys (`.ludeo/tools/bp_inspector.py`, `RunBPInspector.bat`, `BuildAndPackage.bat`, `SetupLudeoEnv.ps1`, `run.bat.template`, and `Plugins/LudeoBPInspector/`) are verbatim copies of the skill's `tools/`. Refreshing a stale copy to match the current skill version is maintenance, not destruction — do it without asking (see "Tools freshness" in Step 1). Only ask first if the deployed copy contains project-local modifications (it differs from the skill copy in ways that reference project-specific names/paths); then show the diff.

---

## Absolute Paths and Bash Safety

**Always use absolute paths** for all filesystem operations. The bash tool does not preserve working directory between calls — a `cd` in one command does not affect the next. When operating inside a subdirectory (e.g. the plugin), `cd` using the full absolute project path and combine subsequent commands with `&&` in a single bash call.

- Never conclude that files or directories are missing based on a single failed `ls` or path check. Always verify with an absolute path before taking any action.
- **Never run a destructive VCS command speculatively.** Confirm state with at least two independent checks (absolute paths) before any irreversible command — `git reset` / `git checkout --` / `git submodule deinit` / `rm -rf` (git), or `p4 revert` / `p4 sync -f` (Perforce). See `references/vcs/git.md` / `references/vcs/p4.md` → `guard_destructive`.

## VCS-Aware File Edits

This skill works on projects under **git**, **Subversion (svn)**, or **Perforce (p4)**. The VCS is detected in Phase 1 and recorded in `integration.json → vcs.type`; every session loads the matching `references/vcs/<type>.md`. All version-control work goes through the named operations in `references/vcs/README.md` — never hardcode `git`.

**If `vcs.type == "p4"`, you MUST open a file for edit before writing it.** A Perforce workspace is read-only by default, so the Write/Edit tools fail on any tracked file until it is opened. Run `ensure_editable(path)` — the Perforce MCP `edit`/`add` tool, or `p4 edit` / `p4 add` — before **every** Write/Edit, including `.ludeo/` state files and any `.uasset` the BP Inspector modifies. See `references/vcs/p4.md`.

---

## Phase Map (1–9)

| Phase | Name | Reference File |
|-------|------|----------------|
| 1 | Know Your Game | `references/phase-01-know-your-game.md` |
| 2 | Mapping | `references/phase-02-mapping.md` |
| 3 | Lifecycle + Non-Gameplay | `references/phase-03-lifecycle.md` |
| 4 | Map Game Objects (slice) | `references/phase-04-map-objects.md` |
| 5 | Tracking & Restore (slice) | `references/phase-05-tracking-restore.md` |
| 6 | Actions | `references/phase-06-actions.md` |
| 7 | Verification & Cloud | `references/phase-07-verification-cloud.md` |
| 8 | Expansion (full game) | `references/phase-08-expansion.md` |
| 9 | Polish & Fix Bugs | `references/phase-09-polish.md` |

---

## Curated Slice Selection

A curated slice is a **specific map + game mode combination** that represents a short, self-contained gameplay moment (2-5 minutes). All MVP work (Phases 4–6) is scoped to this slice.

### AI-Guided Selection Process

During Phase 2 analysis, the skill suggests 2-3 candidate slices:

1. **Find maps** — Glob for `.umap` files in Content/, read level references
2. **Find game modes** — Grep for GameMode subclasses, identify which modes run on which maps
3. **Classify maps by suitability:**
   - **Arena/wave maps** (self-contained combat loop) → best first slice
   - **Story/mission maps** (objectives, dialogue, exploration) → good second slice
   - **Menu/lobby/transition maps** → skip
4. **Estimate action density** — Grep for ability classes, delegate declarations, event enums near each map's associated code
5. **Check external dependencies** — Does the slice need persistent state from outside (loadout, progression, unlocks)? Fewer dependencies = better first slice.
6. **Present candidates** with rationale and recommended pick

### Key Question

Always ask: **"Which map/level do you use for demos or QA testing?"** — this is almost always the right first slice.

### What Makes a Good Slice

- **Self-contained:** Clear start trigger, gameplay loop, and end condition
- **Action-rich:** Multiple significant events fire (kills, pickups, objectives)
- **Few external dependencies:** Doesn't require progression/economy/meta state from outside the slice
- **Representative:** Shows the core gameplay loop that Ludeo will capture

---

## Per-Session Flow

When invoked, execute these steps in order:

### Step 1: Detect State

Read `.ludeo/integration.json` from the target game repo.

**File exists:**

> **Schema check / migration.** If `.ludeo/integration.json` is missing `schemaVersion` or it is below the current baseline (`3`), or shows fallback signs — a `currentStage` key, any `stage`-named field, or a **0-indexed `currentPhase`** (the phases were renumbered to 1-indexed, so `currentPhase: 0` / a `phases` block keyed from `0` means a pre-`3` file) — load `references/migration.md` and run it before proceeding — do not parse the old schema directly.

- Parse `currentPhase` to determine where the integration left off.
- Check the current phase's status:
  - `status: "in_progress"` → resume work for this phase
  - `status: "completed"` → advance to next phase
- **Load `references/vcs/<vcs.type>.md`** (from `integration.json → vcs.type`) before any file write — its rules apply for the whole session. For p4 this means `ensure_editable` before every Write/Edit (see VCS-Aware File Edits above).
- **Tools freshness (every session):** diff each deployed tool in `.ludeo/tools/` (and `Plugins/LudeoBPInspector/Source/**` if deployed) against the skill's `tools/` directory. If a file differs, redeploy the skill copy (rebuild the editor target if C++ plugin sources changed). The skill gains tool capabilities between sessions; a stale deployed copy silently lacks them — agents have repeatedly hand-rolled one-off scripts for capabilities the current tools already had. See `learnings/common-mistakes/redeploy-tools-on-skill-update.md`.
- Load the reference file for the current phase.

**File does not exist → Phase 1 (first run):**
1. Ask the human:
   - Game title
   - Engine version (UE 4.x / 5.x)
   - Game type (FPS, TPS, Action, etc.)
   - "Which map/level do you use for demos or QA testing?" (initial curated slice hint)
   - **Packaging target** — ask it in the integrator's language, not the skill's. The integrator knows their game and UE, and nothing about Ludeo's pipeline; explain every Ludeo-side concept inline (see `learnings/common-mistakes/intake-questions-must-be-jargon-free.md`). Ship this phrasing:
     > "How will this integration eventually run? Three options: **editor-only** — everything stays in the UE editor for now; **packaged** — we also produce a standalone Windows build of the game (UE's normal 'package project' output) and verify it boots, since that's how the integration will really be tested; **cloud-ready** — same packaged build, but prepared for Ludeo's cloud: Ludeo replays highlights by running your game on Ludeo's cloud machines, so the packaged build eventually gets uploaded there. Which fits this project?"
     Accept one of: `editor-only`, `packaged`, `cloud-build`. Record in `integration.json → packagingTarget`. (Agent-side: this gates Phase 3's Tier 2 smoke test — full package + boot; Tier 1 fast build runs regardless. Never put tier/phase jargon in the question itself.) If the answer is `packaged` or `cloud-build` AND the project has no `Source/` directory, flag it immediately so Phase 3 can plan for a minimal game module or a target-generating plugin (e.g., CommonUI) upfront. If CommonUI (or another auto-trigger plugin) IS enabled, this is the UBT auto-generated-targets case — do NOT create a manual `Source/` (it causes CS0101 conflicts). If no auto-trigger plugin is enabled, a minimal `Source/` game module IS required. See `learnings/engine-quirks/bp-only-needs-target-cs-for-packaging.md` and `learnings/engine-quirks/bp-only-packaging-needs-source-module.md`.
2. **Detect the VCS and create an isolation context.** Run `detect_vcs` (`references/vcs/README.md`) to decide **git** vs **svn** vs **p4** — keyed off where the code lives — record `integration.json → vcs`, and load the matching `references/vcs/<type>.md`. Then perform `create_isolation` per that file, confirming the name with the human: a dedicated **branch** for git (`ludeo-integration/<game>`, or the repo's convention); a **long-lived branch** for svn (creation is a server-side commit — human-gated and deferrable, work proceeds in the current working copy and carries over via `svn switch`; the integration branch is permanent, never reintegrated to trunk); or a **task/dev stream** (or pending changelist) for p4. For p4, first verify the workspace is synced and logged in (`p4 info` / `p4 login -s`) — the skill verifies but does not create the client. All integration work (SDK setup, TDD, code) goes in this context.
3. **Acquire the SDK** via `acquire_component` from the loaded `vcs/<type>.md`. Use the Read tool on `<skill-base-dir>/config/sdk-sources.json` for sources. **Resolve the latest release tag from the repo FIRST — never download a version hardcoded in this config or recalled from memory** (a stale/wrong version is a known failure). Unless the human pinned a version, take the latest: with `gh`, confirm via `gh release list -R ludeo-labs/unreal-plugin-releases -L 1` then `gh release download -R ludeo-labs/unreal-plugin-releases -p '*.zip'` (no tag = latest); without `gh`, read `.tag_name` from the `releases/latest` API. See the config's `release.acquireLatest`. Record the resolved tag in `integration.json → sdkSetup.tag`.

   **Preferred path (all VCS):** download the self-contained plugin **release zip** (`ludeoUESDKPlugin.release` — `LudeoUESDK-<tag>.zip`, ~816 MB / ~4 GB extracted) and extract into `Plugins/LudeoUESDK`. It **bundles the C SDK** already populated at `Source/LudeoSDK/SDK/`, so this single download satisfies both components — no separate C SDK step, no submodule, no LFS. For p4, `p4 add` the extracted tree (via the Perforce MCP or CLI). For git, commit it (or use submodules instead — see `vcs/git.md`).

   Then **validate** the extract: `Source/LudeoSDK/SDK/Bin/Win64/Release/LudeoSDK-Win64-Release.dll` (and `Lib/`, `Include/`) must exist. If the project already has `Plugins/LudeoUESDK`, confirm the path instead. Record approach (`method`, `tag`, paths) in `integration.json → sdkSetup`.
4. **Create `.ludeo/` directory structure — only create what's missing:**
   ```
   .ludeo/
   ├── integration.json
   ├── tdd/
   │   └── integration-tdd.md
   └── tools/
       ├── SetupLudeoEnv.ps1
       ├── BuildAndPackage.bat
       ├── run.bat.template
       ├── bp_inspector.py
       ├── RunBPInspector.bat
       └── RunKismetDump.bat
   ```
   **IMPORTANT:** Check each directory and file individually. If `.ludeo/` already exists with partial content (e.g., TDD from a prior session), preserve it. Only create directories/files that don't exist yet. NEVER `rm -rf .ludeo` and recreate. See **Destructive Action Guards** section.
5. **Add this engagement's abstract codename to the learning allowlist now.** Append the codename to `config/learning-policy.json` → `sourceGame` allowlist at Phase 1. Writing any learning requires an allowlisted codename; doing this upfront removes the friction that otherwise stalls the first mid-phase learning. Use an abstract codename, never the real studio/title (a real name is itself a leak).
6. Copy tools from the skill's `tools/` directory into `.ludeo/tools/` — copy if missing; if present but different from the skill copy, refresh it (stale tools silently lack current capabilities — see "Tools freshness" in Step 1):
   - `SetupLudeoEnv.ps1` — environment variable setup for running the packaged build
   - `BuildAndPackage.bat` — self-detecting BuildCookRun wrapper for Ludeo cloud builds (detects UE_ROOT from `.uproject` EngineAssociation, GameName from `.uproject` filename, and TargetName from `Source/*.Target.cs` with the `.uproject` name as the BP-only fallback — no substitution needed, copy verbatim; pass `--nopause` when launching programmatically)
   - `run.bat.template` — LudeoCast cloud-launch script template; `BuildAndPackage.bat` instantiates it as `run.bat` at the archived build root (submitted as `executableLaunchPath`)
   - `bp_inspector.py` — UE Editor Python script for Blueprint variable introspection. When the C++ plugin is available, reports variable names, types, default values, SaveGame flags, replication flags, components, and parent class. Falls back to .uasset binary scanning when the plugin is absent.
   - `RunBPInspector.bat` — batch wrapper that auto-detects UE_ROOT (4.x and 5.x) and invokes `bp_inspector.py` via headless editor commandlet. Usage: `RunBPInspector.bat inspect` or `RunBPInspector.bat set-savegame <bp_path> <var_name> true`
   - `RunKismetDump.bat` — batch wrapper for the `LudeoDumpKismet` commandlet (Kismet bytecode dump — see Available Tools below). Only functional once the `LudeoKismetDump` plugin is installed; copy the bat now so it's at hand when a later phase needs it.
7. **Ask the human about Blueprint introspection approach.** Present this choice:

   > "This game has Blueprints. I can inspect BP variables, components, and parent classes in two ways:
   >
   > **Option A — Install the BP Inspector plugin (recommended for BP-heavy games).** I'll add a small C++ Editor plugin to the project, compile it (~2 min), and then automatically read all BP variables (names, types, defaults), SaveGame flags, replication flags, and components. This lets me answer most architecture questions without asking you to open each Blueprint. The plugin is editor-only — zero runtime cost.
   >
   > **Option B — I'll ask you directly.** If you already have the editor open or prefer not to add a plugin, I'll ask specific questions about each Blueprint as needed (parent class, components, variable types, SaveGame flags). This is faster to start but slower per-question.
   >
   > Which do you prefer?"

   **Prerequisite — enable the Python Editor Script Plugin.** `bp_inspector.py` runs through `-ExecutePythonScript`, which silently no-ops (exit 0, no report) if the plugin is off. Before running the inspector, ensure `.uproject` Plugins contains `{"Name": "PythonScriptPlugin", "Enabled": true}` (and `EditorScriptingUtilities`). This is required for BOTH the C++ plugin path and the .uasset fallback path. On Windows, the reliable headless invocation is PowerShell calling `UnrealEditor-Cmd.exe` directly (not a `cmd.exe /c` of the `.bat`, which can fail and whose `pause` hangs non-interactive runners): `& "<UE>/Engine/Binaries/Win64/UnrealEditor-Cmd.exe" "<Game>.uproject" -run=pythonscript -script="<abs path>/bp_inspector.py" -PythonArg="inspect" -unattended -nopause -nosplash`

   **If Option A:**
   - Copy `tools/LudeoBPInspector/` to `<GameRoot>/Plugins/LudeoBPInspector/`
   - Add `{"Name": "LudeoBPInspector", "Enabled": true}` to the `.uproject` Plugins array (if not already present)
   - Build the Editor target: `Build.bat <GameName>Editor Win64 Development <Game>.uproject` (this compiles the plugin alongside the game)
   - If compilation fails, log the error and continue — `bp_inspector.py` will fall back to .uasset scanning automatically. Record `"bpInspectorPlugin": {"available": false}` in `integration.json` → `tools`.

   **If Option B:**
   - Record `"bpInspectorPlugin": {"available": false, "reason": "human-opted-out"}` in `integration.json` → `tools`.
   - Skip plugin deployment. Phase 2 and Phase 4 will use human questions instead of the automated report.

   **Skip this step entirely** if the game is C++-heavy with minimal Blueprint gameplay logic (i.e., `Source/` headers contain all gameplay UPROPERTYs).
8. Initialize `integration.json` — only if the file doesn't exist. If it exists, read it and resume from where it left off.
9. Create empty TDD with header `# Ludeo SDK Integration — Technical Design Document` — only if the TDD file doesn't exist. If it exists, preserve it.
10. **Verify Steam user is in Ludeo environment.** Ask the human: "Has the Steam user been added to the Ludeo Studio Labs environment? Ludeo creation silently fails without this — highlights record but can't convert to Ludeos." Record confirmation in `integration.json` → `sdkSetup.steamUserInEnvironment`.
11. **Verify environment runnability.** Ask the human two questions and record both in `integration.json` → `sdkSetup`:
    - `"Can the curated slice be played WITHOUT the game's live/online backend? If not, does an offline-mode preprocessor gate (e.g., LUDEO_OFFLINE_MODE) exist or need to be built as a prerequisite?"` — Record under `sdkSetup.offlineBackend` as `"works" | "gate-exists:<flag>" | "gate-needed"`. If `gate-needed`, this is a blocker for later phases; flag it.
    - `"What is the exact command/cheat/call to load the curated slice from a cold boot? ServerTravel by map name? A game-specific state-machine call? Editor PIE only?"` — Record under `sdkSetup.sliceLoadCommand`. The default `ServerTravel` may silently no-op for games with custom load paths (e.g., ActionGame uses `UGameStateMachine::RequestSoloGame`).
12. **Run the kickoff intake questionnaire.** Read `<skill-base-dir>/references/phase-01-know-your-game.md` and walk the 4 question groups with the human. Target 20-30 minutes. Record answers to `integration.json` → `intake`. Unknown answers become risks, not blockers — they resurface at the phase that gates on them. Phases 3 and 4 pre-flight checklists read this block and fail if required fields are missing.
13. Transition immediately to Phase 2.

### Available Tools

#### BP Inspector (`RunBPInspector.bat`)

Reads and writes Blueprint variable metadata. Runs inside UE Editor (headless). Requires the Python Editor Script Plugin (enable it in Phase 1 step 7 — without it the script silently no-ops). When the LudeoBPInspector C++ plugin is also deployed (Phase 1 step 7), provides full introspection including SaveGame flags, replication flags, default values, and components. Without the plugin, falls back to .uasset binary scanning (variable names and types only — no flags, defaults, or components).

**Commands:**

| Command | What it does |
|---------|-------------|
| `RunBPInspector.bat inspect` | Scan all gameplay BPs, write report to `.ludeo/bp-inspection-report.json`. Takes 30-60s (editor boot). Run via `run_in_background: true`. |
| `RunBPInspector.bat set-savegame <bp_path> <var_name> true\|false` | Set or clear the SaveGame flag on a single BP variable. Requires the C++ plugin. Compiles and saves the BP automatically. |
| `RunBPInspector.bat set-savegame-batch <bp1> <var1> <bp2> <var2> ...` | Set SaveGame flag on multiple variables in one editor session (~30-60s total instead of per variable). Groups by BP for efficiency. Always sets to true. Requires C++ plugin. **Use this instead of chaining multiple `set-savegame` calls.** |
| `RunBPInspector.bat graph` | Scan all gameplay BPs for functions, events, and call graphs. Writes to `.ludeo/bp-graph-report.json`. Requires C++ plugin. Run via `run_in_background: true`. |
| `RunBPInspector.bat graph-function <bp_path> <function_name>` | Get the exec-pin call graph for a single function or event in one BP. Prints ordered node list to log file. Requires C++ plugin. |
| `RunBPInspector.bat inspect-path [--resolve-inherited] <bp_path> [<bp_path> ...]` | Full unfiltered dump (parent, vars+defaults, components, functions, events) of specific BPs by path. Use for plain-`Actor` BPs the `inspect` filter excludes (spawners, weapon actors, AI managers, pickups). Writes `.ludeo/path-inspection.json`. Add `--resolve-inherited` to also walk the BP parent chain and include base-class-declared (inherited) variables — use when a leaf dump shows no expected state (e.g. Health/IsDead) because it's declared on a BP base class. |
| `RunBPInspector.bat inspect-level <map_path> [<focus_keyword> ...]` | Load a map and enumerate actually-placed actors: class histogram + per-actor BP properties for spawn/AI actors. Reveals what is placed/spawned in the curated map (which asset-only `inspect` cannot see). Writes `.ludeo/level-inspection.json`. |
| `RunBPInspector.bat inspect-func-sigs <bp_path> [<bp_path> ...]` | Dump each BP function's input/output pin signatures — the parameter shapes that `graph`/`graph-function` (node titles) and `inspect` (variables) don't show. Use when you must call a game function (e.g. an inventory `AddItem`) and need its exact pins. Writes `.ludeo/func-sigs.json`. Requires C++ plugin. |

**Report JSON shapes** (all commands): see `references/bp-inspector-reference.md` before consuming any report.

#### Kismet Bytecode Dump (`RunKismetDump.bat` + `tools/LudeoKismetDump/` plugin)

Disassembles compiled Blueprint bytecode — level scripts and gameplay BPs — to readable text. Complements the BP Inspector: the Inspector reports variables/signatures/exec-graphs per asset, the Kismet dump shows the *actual compiled logic* including bound-event wiring (`BndEvt__` → which placed actor, which delegate) and latent nodes (Delays, timelines) that property inspection can't see. This is the tool for "the mission script doesn't continue after restore" class of problems.

Deploy like the BP Inspector plugin: copy `tools/LudeoKismetDump/` to `<GameRoot>/Plugins/`, add `{"Name": "LudeoKismetDump", "Enabled": true}` to the `.uproject`, build the editor target. Compiles itself out of Shipping/Test.

| Command | What it does |
|---------|-------------|
| `RunKismetDump.bat <Project>.uproject [-Maps=Sub1,Sub2] [-Classes=Sub1] [-OutDir=path]` | Offline all-maps dump (headless commandlet). Per level-script class writes `.kismet.txt` (disassembly), `.events.txt` (bound-event inventory + compiled-in dynamic bindings), `.vars.txt` (variables + Replicated/SaveGame flags), plus per-map `_PlacedActorBindings.txt` (serialized delegate invocation lists of placed actors — resolves which delegate *property* each `BndEvt__` stub binds; stub names only carry the signature) under `<ProjectSaved>/LudeoKismet/<Map>/`. Walks classic streaming sublevels (scripting often lives in a dedicated `_LSS` sublevel). Run via `run_in_background: true` (~1-5 min). |
| `... -AllPaths` (or `-Paths=/Game,/Foo`) | **Required for GameFeature titles (Lyra-style):** their maps mount at `/<PluginName>`, not `/Game`, so a default run silently misses every mission map. `-AllPaths` scans all project-mounted content roots. |
| `LudeoKismet.DisassembleBP <ClassSubstring>` (in-game console) | Ad-hoc disassembly of loaded classes during a live session. |
| `LudeoKismet.DumpDelegateBindings <ClassSubstring>` (in-game console) | Live invocation list of every multicast delegate on matching world actors — "who listens to this event right now". Run after gameplay starts. |

**When to use & how to read the dumps:** `references/kismet-bytecode-analysis.md` — covers the event-stub → ubergraph-offset reading technique and the restore-semantics patterns (arm-vs-grant delegate handlers, idempotent re-derivers, load-bearing latent state). Most valuable at Phase 8/9 (Player Flow restore of scripted mission progression) and per-new-map scripting-surface triage. Known limitation: UE5 World Partition cells aren't walked (persistent-level scripts still dump).

**When to use:**
- **Phase 2:** Read the `inspect` report to classify save system (SaveGame flags present → Group 1) and answer structural questions. Run `graph` to understand BP logic flow (what BeginPlay calls, what event handlers do) without asking the human to screenshot graphs. Use `graph-function` for targeted queries on specific functions.
- **Phase 5:** Set SaveGame flags on curated slice variables after human approves the variable list. Re-run inspect to verify.
- **Phase 8:** Discover additional variables for enrichment. Set SaveGame flags on newly identified variables.
- **Any phase:** Answer BP structural or behavioral questions without asking the human to open the editor

**Do NOT** create console commands or ask the human to manually check SaveGame checkboxes. The `set-savegame` / `set-savegame-batch` commands do this headlessly. When flagging 2+ variables, always use `set-savegame-batch` to avoid multiple editor boots.

**Log file:** All commands write human-readable progress and errors to `.ludeo/bp-inspector-log.txt` (overwritten each run). Read this file after a `run_in_background` headless run completes — headless UE swallows stdout.

### Step 2: Load Reference File

Read the reference file for the current phase from the phase map table above. Each reference file contains the phase's analysis checklist, patterns, questions, output template, and common mistakes.

**SDK field name drift warning:** Reference files contain code skeletons with SDK method signatures and field names. These can drift from the actual SDK headers. Before copying any code from a reference file, **grep the SDK headers** (`Plugins/LudeoUESDK/Source/`) for the exact field names and method signatures. If the reference says `Params.ApiKey` but the SDK header says `Params.APIKey`, the SDK header wins.

### Step 3: Load Learnings

The corpus is large (~250 files) and grows with every integration — reading every file each phase burns the context the phase's real work needs. Load it **index-first**:

1. **Read `<skill-base-dir>/learnings/INDEX.md` in full.** One line per learning: `path | tier | phase | hook`. The hook is the learning's precondition question (or its title when it has none).
2. **Read the full body of every entry whose `phase` matches the current phase** — regardless of tier, no exceptions. Phase tags are mostly right but conservative, so this is the floor, not the whole job.
3. **Scan every other index line's hook against what you know about this project** (BP-only? streamed maps? GAS? packaged/cloud target? no save system?) and read the body of anything plausibly relevant. **Err toward reading** — a body read is cheap; wrongly skipping a learning is how integrations break. An index line is a pointer, not the lesson: never cite or apply a learning from its index line alone.
4. **Re-query the index mid-phase.** When you hit a new topic (packaging, pause, inventory restore, activation timing…), grep the index for it and read the matches before improvising.
5. **Cross-check completeness:** compare the index's `Total:` count against `Glob(pattern: "**/*.md", path: "<skill-base-dir>/learnings")` (glob from the learnings dir directly — a `learnings/**` prefix fails on Windows; subtract INDEX.md itself). If files exist that the index misses, read them too — the index is stale; regenerate it (`node scripts/generate-learnings-index.mjs`) or append the missing lines.

**Anti-pattern (the reason for rules 2-3):** skimming names/hooks and deciding "these don't apply" without reading bodies. This is how the FPSGameStarterKit agent skipped `missing-explicit-auth.md` — it saw "explicit auth" in the title, assumed it didn't apply, and wrote broken activation code. The hook line exists to catch your attention, not to clear a learning as irrelevant.

Filter by tier: `universal` and `generalizable` apply across games. Load `game-specific` only from the same game.

**Tier semantics (STRICT):**

- **`universal`** = the advice applies to **every** UE Ludeo integration with **no preconditions**. If a learning has ANY project-specific condition under which the advice does not apply, it is NOT universal.
- **`generalizable`** = the advice applies only when specific preconditions hold. The learning MUST state its preconditions in the first section of the body (labeled "Precondition" or equivalent). Readers must verify the preconditions in the current project before applying the advice.
- **`game-specific`** = the advice is tied to a particular game's architecture and is not intended for reuse on other games.

**Before citing any learning in a decision, the agent MUST:**

1. Read the **full body** of the learning, not just the title or summary.
2. Identify the learning's **precondition** (explicit if `generalizable`, verify there is genuinely none if `universal`).
3. **Verify the precondition holds in the current project** with concrete evidence (files read, human questions asked and answered, empirical tests run).
4. If the precondition cannot be verified from evidence in the current project, **the learning does not apply** — do not cite it, do not apply its conclusion.
5. When recording the decision in `integration.json`, cite the learning **and the evidence** that its precondition holds in this project. A decision rationale that only names a learning without demonstrating precondition match is insufficient.

**Red flag for the agent to stop:** If a learning's conclusion is absolute ("the ONLY working approach", "ALL approaches failed", "NEVER do X") AND it is `tier: universal`, be suspicious — engineering rules with "only / always / never" almost always have preconditions. Check whether the learning should actually be `generalizable` before applying it.

**Before checking classifications in Phase 2 (or any later phase)**, the agent MUST read `references/reference-sample-catalog.md` and check for matches. A sample match is stronger evidence than grep-based inference and should be the starting point, not an afterthought.

For the meta-rule itself and the incidents that motivated it, see `learnings/common-mistakes/do-not-trust-learning-without-verifying-precondition.md`.

### Step 4: Check MCP Servers

The skill's primary, always-current source of SDK detail is the **`sdk-docs`** MCP server — the server that **searches the Ludeo SDK documentation** (method signatures, parameter structs, callback chains, concepts). It ships with the skill and runs on the integrator's machine. Set it up once, before doing any SDK work:

> **If `sdk-docs` is not already connected**, wire it up from the bundled template, then continue. It is hosted (HTTP) at `https://ludeo-mcps-sdk-docs.ludeo.com/mcp` and needs an `X-User-Name` header set to your Ludeo username (the local-part of your Ludeo email, e.g. `jane.doe`) — it identifies the caller.
> - **Claude Code:** copy the `sdk-docs` entry from `<skill-base-dir>/config/mcp_config.template.json` into the project's `.mcp.json` (or run `claude mcp add`), set `X-User-Name` to your Ludeo username, then start a fresh session so the server connects.
> - **Other agents:** add the same entry to your runtime's MCP config.
> - If you cannot connect it, tell the human and fall back to the bundled `references/sdk-reference/` files — but **say so explicitly**, since the bundled copy covers concepts only and can drift from the live SDK.

Check for available MCP servers:
- **`sdk-docs`** — Primary. **Search it for any SDK API detail, at any phase**, instead of guessing. Fallback: bundled `references/sdk-reference/` files (concepts only).
- **`ludeo-context`** — If available, use for company knowledge, QA workflows, repo context. Particularly useful for Phase 2 (mapping analysis) and Phases 5–6 (tracking/restore and actions discovery from QA event lists).
- **Perforce MCP** (only when `vcs.type == "p4"`) — If a Perforce MCP server is connected (the official Perforce P4 MCP is recommended), use its tools for `edit`/`add`/`shelve`/`submit` and stream ops. Record the server name in `integration.json → vcs.p4.mcp`. If none is connected, set it to `null` and fall back to the raw `p4` CLI (see `references/vcs/p4.md`).

If a needed MCP is unavailable, set it up from `config/mcp_config.template.json` (above), or inform the human and proceed with the bundled fallback.

### Step 5: Execute Phase Work

Follow the loaded reference file's guidance:
1. **Create TodoWrite items for each analysis sub-item** (e.g., 3.1, 3.2, ..., 3.6), not just the phase as a whole. An unchecked "Verify API exports for all hook points" todo prevents skipping critical steps.
2. Analyze the game codebase (read files, grep patterns per the analysis checklist)
3. Present your plan in chat — what you'll implement, which hook points, key decisions
4. Get quick approval from the human ("looks good" / "change X")
5. **Complete the pre-flight checklist** at the top of the reference file's Implementation Guidance section before writing any code
6. Implement the phase's code
7. Record decisions and findings in `integration.json`

For Phases 3+: prefer inference over questions. Analyze the code, make a decision, implement. Only ask when truly stuck or when the choice is irreversible.

### Step 6: Compile-Fix (Hard Gate)

**How to compile:** Read the learning file `learnings/engine-quirks/how-to-compile-ue-from-cli.md` for CLI compilation instructions. Use **UnrealBuildTool (Option 1)** for the compile-fix loop — it's the fastest. Run builds with `run_in_background: true` since they take 15-120 seconds. On first use, detect UE_ROOT from the `.uproject` EngineAssociation field and the target name from `Source/<GameName>/<GameName>.Target.cs`.

1. Generate implementation code based on the analysis
2. Enable the plugin in the game's `.uproject` file
3. **Compile-fix loop (HARD GATE):**
   - Build with plugin disabled (baseline) — verify core game modifications compile without the plugin
   - Build with plugin enabled — verify Ludeo code compiles
   - Extract errors: `grep "error C" <output>` for compiler errors, `grep "error LNK" <output>` for linker errors
   - Read the FIRST error, fix it, rebuild. Repeat up to 10 times.
   - **Build after each new source file.** Do not write the next file until the current one compiles.
   - **If you cannot compile locally:** you MUST still enable the p

…(truncated)
