AI RPG Export & Import Pipeline
The @world-forge/export-ai-rpg package converts a WorldProject into a set of JSON files that ai-rpg-engine can load directly.
Pipeline Steps
Section titled “Pipeline Steps”- Validate —
validateProject()runs 89 structural checks. If any fail, export aborts with error details. - Convert zones —
Zone[]becomesZoneDefinition[]with description as TextBlock, exits, neighbors, hazards. - Convert districts —
District[]becomesDistrictDefinition[]with safety mapped to surveillance. - Convert entities —
EntityPlacement[]becomesEntityBlueprint[]with role-based defaults, authored stats/resources/AI. - Convert items —
ItemPlacement[]becomesItemDefinition[]with slot, rarity, modifiers, provenance. - Convert dialogues —
DialogueDefinition[]passes through to engine’s matching type. - Convert player template —
PlayerTemplatebecomesExportedPlayerTemplatewith stats, inventory, equipment, spawn. - Convert build catalog —
BuildCatalogDefinitionbecomesExportedBuildCatalogwith archetypes, backgrounds, traits, disciplines. - Convert progression trees —
ProgressionTreeDefinition[]maps nodes with requirements and effects. - Build manifest — game ID, title, modules, content pack references.
- Build pack metadata — genres, tones, difficulty, narrator tone, authoring mode tag.
- Collect warnings — missing player template, build catalog, progression trees, landmarks, factions, hotspots.
- Collect assets — asset manifest and zone/entity/item/landmark bindings are attached to the ExportResult for round-trip preservation.
- Collect asset packs — asset pack definitions are attached to the ExportResult when present.
Output Format
Section titled “Output Format”The export produces a ContentPack with all authored domains plus manifest and metadata:
type ContentPack = { _debug?: ExportDebugBlock; // only with --profile debug schemaVersion?: string; // default-on; --no-emit-schema-version strips it entities: EntityBlueprint[]; placements: ExportedPlacement[]; // where entities stand (not on the blueprint) zones: ExportedZone[]; // includes entryGate, not bare ZoneDefinition districts: DistrictDefinition[]; dialogues: DialogueDefinition[]; items: ItemDefinition[]; playerTemplate?: ExportedPlayerTemplate; buildCatalog?: ExportedBuildCatalog; progressionTrees: ProgressionTreeDefinition[]; encounterAnchors: EncounterAnchor[]; factionPresences: FactionPresence[]; pressureHotspots: PressureHotspot[]; hazardDefinitions: HazardDefinition[]; lootTables: LootTable[]; craftingStations: CraftingStation[]; marketNodes: MarketNode[];};CLI Usage
Section titled “CLI Usage”# Export to directorynpx world-forge-export project.json --out ./my-pack
# Validate only (no output files)npx world-forge-export project.json --validate-only
# Dry-run: validate + report sizes, never write (--out is mutually exclusive)npx world-forge-export project.json --dry-run
# Debug profile (adds _debug block, keeps every fidelity entry)npx world-forge-export project.json --out ./my-pack --profile debug
# Strip ContentPack.schemaVersion (default is to emit it)npx world-forge-export project.json --out ./my-pack --no-emit-schema-version
# Verbose output (detailed conversion log)npx world-forge-export project.json --out ./my-pack --verbose
# Import a pack directory back to WorldProject JSONnpx world-forge-export --import ./my-pack --out ./round-trip
# Import content-pack.json + pack-meta.json + manifest.json (and sidecars)npx world-forge-export --from-pack ./my-pack --out ./round-tripProgrammatic Usage
Section titled “Programmatic Usage”import { exportToEngine } from '@world-forge/export-ai-rpg';
const result = exportToEngine(myProject);
if (!result.success) { // Validation failed console.error(result.errors);} else { // Success const { contentPack, manifest, packMeta, warnings, assets, assetBindings, assetPacks } = result;}Entity Conversion Details
Section titled “Entity Conversion Details”Role-based defaults are applied when the author hasn’t specified values:
| Role | Engine Type | Default AI | Default Tags |
|---|---|---|---|
| npc | npc | passive | — |
| enemy | enemy | aggressive | hostile |
| merchant | npc | passive | merchant, trader |
| companion | npc | follower | recruitable, companion |
| boss | enemy | territorial | hostile, boss, elite |
Authored values always override defaults. For example, if you set ai.profileId: 'aggressive' on a boss, it uses that instead of the default 'territorial'.
Import Pipeline
Section titled “Import Pipeline”World Forge can import exported JSON back into the editor. The import pipeline reverses the export process with 8 converters:
| Converter | Input | Output |
|---|---|---|
importZones |
ZoneDefinition[] | Zone[] |
importDistricts |
DistrictDefinition[] | District[] |
importEntities |
EntityBlueprint[] | EntityPlacement[] |
importItems |
ItemDefinition[] | ItemPlacement[] |
importDialogues |
DialogueDefinition[] | DialogueDefinition[] |
importPlayerTemplate |
ExportedPlayerTemplate | PlayerTemplate |
importBuildCatalog |
ExportedBuildCatalog | BuildCatalogDefinition |
importProgressionTrees |
ProgressionTreeDefinition[] | ProgressionTreeDefinition[] |
The importProject() function auto-detects the input format (WorldProject, ExportResult, or ContentPack) and orchestrates all converters.
import { importProject } from '@world-forge/export-ai-rpg';
const result = importProject(jsonString);
if (result.success) { const { project, format, lossless, fidelityReport } = result;}Supported Formats
Section titled “Supported Formats”- WorldProject — lossless round-trip, no conversion needed
- ExportResult —
{ contentPack, manifest, packMeta, assets, assetBindings }fromexportToEngine() - ContentPack — engine content without manifest/metadata wrapper
- ProjectBundle — portable
.wfproject.jsonfile exported from the editor (lossless)
The Measured Export Contract (v4.6.0)
Section titled “The Measured Export Contract (v4.6.0)”An exporter that runs is not the same thing as a world that boots. v4.6.0 makes the difference measurable instead of assumed.
The alignment audit
Section titled “The alignment audit”docs/c0-alignment/ holds a generated, checked-in export table: a leaf-path
differ walks every authored field in a fixture project, exports it, and records
which fields actually arrive in the ContentPack — lossless, approximated, or
dropped. It is regenerated and verified on every test run, so a converter that
silently stops carrying a field fails a test rather than going unnoticed.
This exists because the alternative had already happened. Two required fields —
craftingStations and marketNodes — were dropped by the exporter with no warning
and no fidelity entry, and returned as empty arrays on import, so a round trip
erased authored town economy in both directions without a single failing test.
Manifest truth
Section titled “Manifest truth”The emitted manifest carries a real engine semver range, real module ids, a content hash over the simulation-affecting content, and compiled exit conditions — values that were previously nominal.
Module ids are gated on real content. A pack with no crafting stations no longer declares the crafting module active. Claiming a module that has nothing to act on is worse than dropping the content quietly, because it tells the runtime to expect something that is not there.
What crosses
Section titled “What crosses”Per-entity placements with compiled spawn conditions, typed hazards, entry gates, and scene descriptors all reach the engine’s content pack — not just the schema.
Fidelity Reporting
Section titled “Fidelity Reporting”Every import produces a structured FidelityReport that tracks exactly what happened to each piece of data during conversion. Each entry has:
- level —
lossless,approximated, ordropped - domain — which system was affected (zones, districts, entities, items, etc.)
- severity —
info,warning, orerror - reason — machine-stable key for programmatic use
Common fidelity entries:
| Reason Key | Level | Description |
|---|---|---|
grid-auto-generated |
approximated | Zone grid positions auto-generated (engine doesn’t store spatial layout) |
surveillance-to-safety |
approximated | District safety reverse-mapped from engine’s surveillance metric |
economy-data-lost |
dropped | District economy profile not stored in engine format |
zone-placement-round-robin |
approximated | Entities assigned to zones via round-robin (original zones unknown) |
role-reverse-mapped |
approximated | Entity role inferred from engine tags |
textblock-to-string |
approximated | Dialogue text normalized from TextBlock arrays to strings |
visual-layers-dropped |
dropped | Visual layers (tiles, props, ambient) not stored in engine format |
assets-recovered |
lossless | Asset manifest and bindings restored from ExportResult |
asset-packs-recovered |
lossless | Asset packs restored from ExportResult |
assets-dropped |
dropped | Assets not available in bare ContentPack format |
asset-packs-dropped |
dropped | Asset packs not available in bare ContentPack format |
mode-inferred |
approximated | Authoring mode inferred from connection kinds and grid area |
The report includes a summary with overall lossless percentage and per-domain breakdowns, displayed in the editor’s Import Summary panel.
Mode Preservation
Section titled “Mode Preservation”The export pipeline stores the project’s authoring mode as a mode:<value> tag in PackMetadata. On import:
- ExportResult — mode is recovered from the
mode:tag inpackMeta.tags(lossless) - ContentPack / pre-mode projects —
inferMode()uses heuristics to recover the likely mode:channelorrouteconnections → oceanwarpordockingconnections → spacetrailconnections with camp/wild zone tags → wilderness- Grid area ≤ 400 → interior
- Grid area ≥ 4000 → world
- Fallback → dungeon
Inferred modes generate a mode-inferred fidelity entry at the approximated level.
Dogfood: Chapel Threshold
Section titled “Dogfood: Chapel Threshold”The dogfood/ directory contains a full export test using the Chapel Threshold fixture — 5 zones, 2 districts, 4 entities, 3 items, 1 dialogue, 1 player template, 1 build catalog, 2 progression trees. Running npx tsx dogfood/chapel-threshold.ts exports the fixture and performs a gap analysis against engine expectations. As of v1.2, the gap analysis reports zero gaps — full engine handshake.
