diff --git a/.changeset/four-bugs-warn.md b/.changeset/four-bugs-warn.md
new file mode 100644
index 0000000000..5012c32c54
--- /dev/null
+++ b/.changeset/four-bugs-warn.md
@@ -0,0 +1,5 @@
+---
+"@latticexyz/world": patch
+---
+
+Refactored worldgen in preparation for multiple namespaces.
diff --git a/e2e/packages/contracts/src/codegen/world/IWorld.sol b/e2e/packages/contracts/src/codegen/world/IWorld.sol
index da4671b1e1..e52b4ae995 100644
--- a/e2e/packages/contracts/src/codegen/world/IWorld.sol
+++ b/e2e/packages/contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { ICustomErrorsSystem } from "./ICustomErrorsSystem.sol";
 import { ILibWrapperSystem } from "./ILibWrapperSystem.sol";
 import { INumberListSystem } from "./INumberListSystem.sol";
diff --git a/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol b/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol
index 6eb7845c59..93717f817d 100644
--- a/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol
+++ b/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { IChatSystem } from "./IChatSystem.sol";
 import { IIncrementSystem } from "./IIncrementSystem.sol";
 import { IInventorySystem } from "./IInventorySystem.sol";
diff --git a/examples/multiple-namespaces/src/codegen/world/IWorld.sol b/examples/multiple-namespaces/src/codegen/world/IWorld.sol
index af8a6d4c3a..f42e816eff 100644
--- a/examples/multiple-namespaces/src/codegen/world/IWorld.sol
+++ b/examples/multiple-namespaces/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { IHealSystem } from "./IHealSystem.sol";
 import { IMoveSystem } from "./IMoveSystem.sol";
 
diff --git a/package.json b/package.json
index 0191d6afa9..2d4be4a2dd 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "url": "https://github.com/latticexyz/mud.git"
   },
   "scripts": {
-    "all-build": "for dir in packages/store packages/world packages/world-modules packages/cli test/mock-game-contracts e2e/packages/contracts examples/*/packages/contracts templates/*/packages/contracts; do (cd \"$dir\" && pwd && pnpm build); done",
+    "all-build": "for dir in packages/store packages/world packages/world-modules packages/cli test/mock-game-contracts e2e/packages/contracts examples/*/packages/contracts examples/multiple-namespaces templates/*/packages/contracts; do (cd \"$dir\" && pwd && pnpm build); done",
     "all-install": "for dir in . docs e2e examples/* templates/*; do (cd \"$dir\" && pwd && pnpm install); done",
     "bench": "pnpm run --recursive bench",
     "build": "turbo run build",
diff --git a/packages/common/src/codegen/render-solidity/common.ts b/packages/common/src/codegen/render-solidity/common.ts
index f73a5162d7..5e339bee9b 100644
--- a/packages/common/src/codegen/render-solidity/common.ts
+++ b/packages/common/src/codegen/render-solidity/common.ts
@@ -1,13 +1,4 @@
-import path from "path";
-import {
-  AbsoluteImportDatum,
-  RelativeImportDatum,
-  ImportDatum,
-  StaticResourceData,
-  RenderKeyTuple,
-  RenderType,
-} from "./types";
-import { posixPath } from "../utils";
+import { ImportDatum, StaticResourceData, RenderKeyTuple, RenderType } from "./types";
 import { resourceToHex } from "../../resourceToHex";
 import { hexToResource } from "../../hexToResource";
 import { renderImportPath } from "./renderImportPath";
@@ -73,54 +64,11 @@ export function renderCommonData({
   };
 }
 
-/**
- * For 2 paths which are relative to a common root, create a relative import path from one to another
- * @deprecated Use `renderImportPath` instead.
- */
-export function solidityRelativeImportPath(fromPath: string, usedInPath: string): string {
-  // 1st "./" must be added because path strips it,
-  // but solidity expects it unless there's "../" ("./../" is fine).
-  // 2nd and 3rd "./" forcefully avoid absolute paths (everything is relative to `src`).
-  return posixPath("./" + path.relative("./" + usedInPath, "./" + fromPath));
-}
-
 /**
  * Aggregates, deduplicates and renders imports for symbols per path.
  * Identical symbols from different paths are NOT handled, they should be checked before rendering.
  */
 export function renderImports(imports: ImportDatum[]): string {
-  return renderAbsoluteImports(
-    imports.map((importDatum) => {
-      if ("path" in importDatum) {
-        return importDatum;
-      } else {
-        return {
-          symbol: importDatum.symbol,
-          path: solidityRelativeImportPath(importDatum.fromPath, importDatum.usedInPath),
-        };
-      }
-    }),
-  );
-}
-
-/**
- * Aggregates, deduplicates and renders imports for symbols per path.
- * Identical symbols from different paths are NOT handled, they should be checked before rendering.
- */
-export function renderRelativeImports(imports: RelativeImportDatum[]): string {
-  return renderAbsoluteImports(
-    imports.map(({ symbol, fromPath, usedInPath }) => ({
-      symbol,
-      path: solidityRelativeImportPath(fromPath, usedInPath),
-    })),
-  );
-}
-
-/**
- * Aggregates, deduplicates and renders imports for symbols per path.
- * Identical symbols from different paths are NOT handled, they should be checked before rendering.
- */
-export function renderAbsoluteImports(imports: AbsoluteImportDatum[]): string {
   // Aggregate symbols by import path, also deduplicating them
   const aggregatedImports = new Map<string, Set<string>>();
   for (const { symbol, path } of imports) {
diff --git a/packages/common/src/codegen/render-solidity/types.ts b/packages/common/src/codegen/render-solidity/types.ts
index e61da22518..6eee0afb0b 100644
--- a/packages/common/src/codegen/render-solidity/types.ts
+++ b/packages/common/src/codegen/render-solidity/types.ts
@@ -1,15 +1,7 @@
-export interface AbsoluteImportDatum {
+export type ImportDatum = {
   symbol: string;
   path: string;
-}
-
-export interface RelativeImportDatum {
-  symbol: string;
-  fromPath: string;
-  usedInPath: string;
-}
-
-export type ImportDatum = AbsoluteImportDatum | RelativeImportDatum;
+};
 
 export interface StaticResourceData {
   /** Table namespace string */
diff --git a/packages/common/src/codegen/utils/index.ts b/packages/common/src/codegen/utils/index.ts
index 574bb1fbd4..6e24e2081d 100644
--- a/packages/common/src/codegen/utils/index.ts
+++ b/packages/common/src/codegen/utils/index.ts
@@ -3,4 +3,3 @@ export * from "./extractUserTypes";
 export * from "./format";
 export * from "./formatAndWrite";
 export * from "./loadUserTypesFile";
-export * from "./posixPath";
diff --git a/packages/common/src/codegen/utils/posixPath.ts b/packages/common/src/codegen/utils/posixPath.ts
deleted file mode 100644
index c267fbcab4..0000000000
--- a/packages/common/src/codegen/utils/posixPath.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Explicitly normalize a given path to a posix path (using `/` as separator).
- * This should be used for generating Solidity files that will be consumed by solc,
- * because solc expects `/` as path separator, but path.join produces `\` if the user is on windows.
- */
-export function posixPath(path: string): string {
-  return path.replace(/\\/g, "/");
-}
diff --git a/packages/store/ts/codegen/getTableOptions.ts b/packages/store/ts/codegen/getTableOptions.ts
index b8c4f66158..e985b7074c 100644
--- a/packages/store/ts/codegen/getTableOptions.ts
+++ b/packages/store/ts/codegen/getTableOptions.ts
@@ -1,18 +1,13 @@
 import path from "path";
 import { SchemaTypeArrayToElement } from "@latticexyz/schema-type/deprecated";
-import {
-  ImportDatum,
-  RenderDynamicField,
-  RenderField,
-  RenderKeyTuple,
-  RenderStaticField,
-} from "@latticexyz/common/codegen";
+import { RenderDynamicField, RenderField, RenderKeyTuple, RenderStaticField } from "@latticexyz/common/codegen";
 import { RenderTableOptions } from "./types";
 import { getSchemaTypeInfo, resolveAbiOrUserType } from "./userType";
 import { Table } from "../config/v2/output";
 import { getKeySchema, getValueSchema } from "@latticexyz/protocol-parser/internal";
 import { UserType } from "./getUserTypes";
 import { isDefined } from "@latticexyz/common/utils";
+import { ImportDatum } from "@latticexyz/common/codegen";
 
 export interface TableOptions {
   /** Path where the file is expected to be written (relative to project root) */
diff --git a/packages/store/ts/codegen/renderTableIndex.ts b/packages/store/ts/codegen/renderTableIndex.ts
index 69f14f5432..7ba1e2634e 100644
--- a/packages/store/ts/codegen/renderTableIndex.ts
+++ b/packages/store/ts/codegen/renderTableIndex.ts
@@ -1,4 +1,4 @@
-import { renderList, renderedSolidityHeader } from "@latticexyz/common/codegen";
+import { renderImportPath, renderList, renderedSolidityHeader } from "@latticexyz/common/codegen";
 import { TableOptions } from "./getTableOptions";
 import path from "node:path/posix";
 
@@ -15,7 +15,7 @@ export function renderTableIndex(codegenIndexPath: string, options: TableOptions
       const imports = [tableName];
       if (structName) imports.push(structName);
 
-      return `import { ${imports.join(", ")} } from "./${path.relative(path.dirname(codegenIndexPath), outputPath)}";`;
+      return `import { ${imports.join(", ")} } from "${renderImportPath("./" + path.relative(path.dirname(codegenIndexPath), outputPath))}";`;
     })}
   `;
 }
diff --git a/packages/store/ts/config/v2/index.ts b/packages/store/ts/config/v2/index.ts
index bc37e67a37..17dfc8a467 100644
--- a/packages/store/ts/config/v2/index.ts
+++ b/packages/store/ts/config/v2/index.ts
@@ -11,4 +11,5 @@ export * from "./defaults";
 export * from "./codegen";
 export * from "./enums";
 export * from "./userTypes";
+export * from "./namespace";
 export * from "./namespacedTables";
diff --git a/packages/store/ts/config/v2/namespace.ts b/packages/store/ts/config/v2/namespace.ts
index 0a4b2e3237..ccbd144480 100644
--- a/packages/store/ts/config/v2/namespace.ts
+++ b/packages/store/ts/config/v2/namespace.ts
@@ -45,7 +45,7 @@ export type resolveNamespace<input, scope extends Scope = AbiTypeScope> = input
 
 export function resolveNamespace<const input extends NamespaceInput, scope extends Scope = AbiTypeScope>(
   input: input,
-  scope: scope,
+  scope: scope = AbiTypeScope as never,
 ): resolveNamespace<input, scope> {
   const label = input.label;
   const namespace = input.namespace ?? label.slice(0, 14);
diff --git a/packages/world/mud.config.ts b/packages/world/mud.config.ts
index 6ce75e044f..6c0607e052 100644
--- a/packages/world/mud.config.ts
+++ b/packages/world/mud.config.ts
@@ -2,7 +2,7 @@ import { defineWorld } from "./ts/config/v2/world";
 
 export default defineWorld({
   codegen: {
-    worldImportPath: "../../",
+    worldImportPath: "./src",
     worldgenDirectory: "interfaces",
     worldInterfaceName: "IBaseWorld",
   },
diff --git a/packages/world/src/codegen/interfaces/IBaseWorld.sol b/packages/world/src/codegen/interfaces/IBaseWorld.sol
index 9d2ba9e356..144be64d0f 100644
--- a/packages/world/src/codegen/interfaces/IBaseWorld.sol
+++ b/packages/world/src/codegen/interfaces/IBaseWorld.sol
@@ -5,7 +5,6 @@ pragma solidity >=0.8.24;
 
 import { IStore } from "@latticexyz/store/src/IStore.sol";
 import { IWorldKernel } from "../../IWorldKernel.sol";
-
 import { IRegistrationSystem } from "./IRegistrationSystem.sol";
 import { IAccessManagementSystem } from "./IAccessManagementSystem.sol";
 import { IBalanceTransferSystem } from "./IBalanceTransferSystem.sol";
diff --git a/packages/world/ts/config/v2/defaults.ts b/packages/world/ts/config/v2/defaults.ts
index 1c1ea0a80f..cc034bdb55 100644
--- a/packages/world/ts/config/v2/defaults.ts
+++ b/packages/world/ts/config/v2/defaults.ts
@@ -20,7 +20,7 @@ export type MODULE_DEFAULTS = typeof MODULE_DEFAULTS;
 export const CODEGEN_DEFAULTS = {
   worldInterfaceName: "IWorld",
   worldgenDirectory: "world",
-  worldImportPath: "@latticexyz/world/src/",
+  worldImportPath: "@latticexyz/world/src",
 } as const satisfies CodegenInput;
 
 export type CODEGEN_DEFAULTS = typeof CODEGEN_DEFAULTS;
diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts
index 15a8964896..f728550dfb 100644
--- a/packages/world/ts/config/v2/input.ts
+++ b/packages/world/ts/config/v2/input.ts
@@ -1,6 +1,7 @@
 import { show } from "@arktype/util";
 import { StoreInput } from "@latticexyz/store/config/v2";
 import { DynamicResolution, ValueWithType } from "./dynamicResolution";
+import { Codegen } from "./output";
 
 export type SystemInput = {
   /**
@@ -85,14 +86,7 @@ export type DeployInput = {
   readonly upgradeableWorldImplementation?: boolean;
 };
 
-export type CodegenInput = {
-  /** The name of the World interface to generate. (Default `IWorld`) */
-  readonly worldInterfaceName?: string;
-  /** Directory to output system and world interfaces of `worldgen` (Default "world") */
-  readonly worldgenDirectory?: string;
-  /** Path for world package imports. Default is "@latticexyz/world/src/" */
-  readonly worldImportPath?: string;
-};
+export type CodegenInput = Partial<Codegen>;
 
 export type WorldInput = show<
   StoreInput & {
diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts
index 964bab090e..120ba132c5 100644
--- a/packages/world/ts/config/v2/output.ts
+++ b/packages/world/ts/config/v2/output.ts
@@ -82,8 +82,10 @@ export type Codegen = {
   /** Directory to output system and world interfaces of `worldgen` (Default "world") */
   readonly worldgenDirectory: string;
   /**
-   * Path for world package imports. Default is "@latticexyz/world/src/"
    * @internal
+   * Absolute import path for a package import or starting with `.` for an import relative to project root dir.
+   *
+   * Defaults to `@latticexyz/world/src` if not set.
    */
   readonly worldImportPath: string;
 };
diff --git a/packages/world/ts/node/findSolidityFiles.ts b/packages/world/ts/node/findSolidityFiles.ts
index 3d37a77360..460a971ff3 100644
--- a/packages/world/ts/node/findSolidityFiles.ts
+++ b/packages/world/ts/node/findSolidityFiles.ts
@@ -1,13 +1,9 @@
 import path from "node:path";
 import { glob } from "glob";
-import { World } from "../config/v2/output";
 
 // TODO: move to common codegen?
-export async function findSolidityFiles({ rootDir, config }: { rootDir: string; config: World }) {
-  const files = await glob(path.join(config.sourceDirectory, "**", "*.sol"), {
-    cwd: rootDir,
-  });
-
+export async function findSolidityFiles({ cwd, pattern = "**" }: { cwd?: string; pattern: string }) {
+  const files = await glob(path.join(pattern, "*.sol"), { cwd });
   return files.sort().map((filename) => ({
     filename,
     basename: path.basename(filename, ".sol"),
diff --git a/packages/world/ts/node/getSystemContracts.ts b/packages/world/ts/node/getSystemContracts.ts
index 93c0aaa81f..8b9e12c497 100644
--- a/packages/world/ts/node/getSystemContracts.ts
+++ b/packages/world/ts/node/getSystemContracts.ts
@@ -1,9 +1,11 @@
+import path from "node:path";
 import { World } from "../config/v2/output";
 import { findSolidityFiles } from "./findSolidityFiles";
 
-export type SolidityContract = {
+export type SystemContract = {
   readonly sourcePath: string;
-  readonly name: string;
+  readonly namespaceLabel: string;
+  readonly systemLabel: string;
 };
 
 export type GetSystemContractsOptions = {
@@ -14,20 +16,43 @@ export type GetSystemContractsOptions = {
 export async function getSystemContracts({
   rootDir,
   config,
-}: GetSystemContractsOptions): Promise<readonly SolidityContract[]> {
-  const solidityFiles = await findSolidityFiles({ rootDir, config });
+}: GetSystemContractsOptions): Promise<readonly SystemContract[]> {
+  // TODO: get this value from config once multiple namespaces are supported
+  const multipleNamespaces = false;
+  const solidityFiles = await findSolidityFiles({
+    cwd: rootDir,
+    pattern: path.join(config.sourceDirectory, "**"),
+  });
 
   return solidityFiles
-    .map((file) => ({
-      sourcePath: file.filename,
-      name: file.basename,
-    }))
     .filter(
       (file) =>
-        file.name.endsWith("System") &&
+        file.basename.endsWith("System") &&
         // exclude the base System contract
-        file.name !== "System" &&
+        file.basename !== "System" &&
         // exclude interfaces
-        !/^I[A-Z]/.test(file.name),
-    );
+        !/^I[A-Z]/.test(file.basename),
+    )
+    .map((file) => {
+      const namespaceLabel = (() => {
+        if (!multipleNamespaces) return config.namespace;
+
+        const relativePath = path.relative(path.join(rootDir, config.sourceDirectory), file.filename);
+        const [namespacesDir, namespaceDir] = relativePath.split(path.sep);
+        if (namespacesDir === "namespaces" && namespaceDir) {
+          return namespaceDir;
+        }
+        // TODO: is this too aggressive? will this be problematic for world-modules (systems independent of namespaces)?
+        // TODO: filter based on excluded systems in config?
+        throw new Error(
+          `Expected system file at "${file.filename}" to be in a namespace directory like "${path.join(config.sourceDirectory, "namespaces/{namespace}", relativePath)}"`,
+        );
+      })();
+
+      return {
+        sourcePath: file.filename,
+        namespaceLabel,
+        systemLabel: file.basename,
+      };
+    });
 }
diff --git a/packages/world/ts/node/render-solidity/renderWorldInterface.ts b/packages/world/ts/node/render-solidity/renderWorldInterface.ts
index 92b3980325..0824b81cc1 100644
--- a/packages/world/ts/node/render-solidity/renderWorldInterface.ts
+++ b/packages/world/ts/node/render-solidity/renderWorldInterface.ts
@@ -1,34 +1,43 @@
-import {
-  renderArguments,
-  renderedSolidityHeader,
-  renderAbsoluteImports,
-  renderRelativeImports,
-  type AbsoluteImportDatum,
-} from "@latticexyz/common/codegen";
-import type { RenderWorldOptions } from "./types";
+import { renderArguments, renderedSolidityHeader, renderImports, type ImportDatum } from "@latticexyz/common/codegen";
 
-export function renderWorldInterface(options: RenderWorldOptions) {
-  const { interfaceName, storeImportPath, worldImportPath, imports } = options;
-  const baseImports: AbsoluteImportDatum[] =
-    interfaceName === "IBaseWorld"
+export type RenderWorldOptions = {
+  /** List of symbols to import, and their file paths */
+  imports: ImportDatum[];
+  /** Name of the interface to render */
+  interfaceName: string;
+  /** Path for store package imports */
+  storeImportPath: string;
+  /** Path for world package imports */
+  worldImportPath: string;
+};
+
+export function renderWorldInterface({
+  interfaceName,
+  storeImportPath,
+  worldImportPath,
+  imports: initialImports,
+}: RenderWorldOptions) {
+  const imports = [
+    ...(interfaceName === "IBaseWorld"
       ? [
           { symbol: "IStore", path: `${storeImportPath}/IStore.sol` },
-          { symbol: "IWorldKernel", path: `${worldImportPath}IWorldKernel.sol` },
+          { symbol: "IWorldKernel", path: `${worldImportPath}/IWorldKernel.sol` },
         ]
       : [
           {
             symbol: "IBaseWorld",
-            path: `${worldImportPath}codegen/interfaces/IBaseWorld.sol`,
+            path: `${worldImportPath}/codegen/interfaces/IBaseWorld.sol`,
           },
-        ];
-  const importSymbols = [...baseImports, ...imports].map(({ symbol }) => symbol);
+        ]),
+    ...initialImports,
+  ];
+
+  const importSymbols = imports.map(({ symbol }) => symbol);
 
   return `
     ${renderedSolidityHeader}
 
-    ${renderAbsoluteImports(baseImports)}
-
-    ${renderRelativeImports(imports)}
+    ${renderImports(imports)}
 
     /**
      * @title ${interfaceName} 
diff --git a/packages/world/ts/node/render-solidity/types.ts b/packages/world/ts/node/render-solidity/types.ts
index ae4317ba6c..d8b9772551 100644
--- a/packages/world/ts/node/render-solidity/types.ts
+++ b/packages/world/ts/node/render-solidity/types.ts
@@ -1,9 +1,4 @@
-import type {
-  ImportDatum,
-  RelativeImportDatum,
-  ContractInterfaceFunction,
-  ContractInterfaceError,
-} from "@latticexyz/common/codegen";
+import type { ImportDatum, ContractInterfaceFunction, ContractInterfaceError } from "@latticexyz/common/codegen";
 
 export interface RenderSystemInterfaceOptions {
   /** List of symbols to import, and their file paths */
@@ -13,14 +8,3 @@ export interface RenderSystemInterfaceOptions {
   functions: ContractInterfaceFunction[];
   errors: ContractInterfaceError[];
 }
-
-export interface RenderWorldOptions {
-  /** List of symbols to import, and their file paths */
-  imports: RelativeImportDatum[];
-  /** Name of the interface to render */
-  interfaceName: string;
-  /** Path for store package imports */
-  storeImportPath: string;
-  /** Path for world package imports */
-  worldImportPath: string;
-}
diff --git a/packages/world/ts/node/render-solidity/worldgen.ts b/packages/world/ts/node/render-solidity/worldgen.ts
index 0ec7be5bd0..76b0ec8944 100644
--- a/packages/world/ts/node/render-solidity/worldgen.ts
+++ b/packages/world/ts/node/render-solidity/worldgen.ts
@@ -1,6 +1,6 @@
-import fs from "fs";
-import path from "path";
-import { formatAndWriteSolidity, contractToInterface, type RelativeImportDatum } from "@latticexyz/common/codegen";
+import fs from "node:fs/promises";
+import path from "node:path";
+import { formatAndWriteSolidity, contractToInterface, type ImportDatum } from "@latticexyz/common/codegen";
 import { renderSystemInterface } from "./renderSystemInterface";
 import { renderWorldInterface } from "./renderWorldInterface";
 import { World as WorldConfig } from "../../config/v2/output";
@@ -15,63 +15,71 @@ export async function worldgen({
   config: WorldConfig;
   clean?: boolean;
 }) {
-  const outDir = path.join(config.sourceDirectory, config.codegen.outputDirectory, config.codegen.worldgenDirectory);
+  const outDir = path.join(
+    rootDir,
+    config.sourceDirectory,
+    config.codegen.outputDirectory,
+    config.codegen.worldgenDirectory,
+  );
 
   if (clean) {
-    fs.rmSync(outDir, { recursive: true, force: true });
+    await fs.rm(outDir, { recursive: true, force: true });
   }
 
-  const systems = await resolveSystems({ rootDir, config });
+  const outputPath = path.join(outDir, config.codegen.worldInterfaceName + ".sol");
 
-  const systemInterfaceImports: RelativeImportDatum[] = [];
-  for (const system of systems) {
-    const data = fs.readFileSync(system.sourcePath, "utf8");
-    // get external funcions from a contract
-    const { functions, errors, symbolImports } = contractToInterface(data, system.label);
-    const imports = symbolImports.map((symbolImport) => {
-      if (symbolImport.path[0] === ".") {
-        // relative import
-        return {
-          symbol: symbolImport.symbol,
-          fromPath: path.join(path.dirname(system.sourcePath), symbolImport.path),
-          usedInPath: outDir,
-        };
-      } else {
-        // absolute import
-        return {
-          symbol: symbolImport.symbol,
-          path: symbolImport.path,
-        };
-      }
-    });
-    const systemInterfaceName = `I${system.label}`;
-    const output = renderSystemInterface({
-      name: systemInterfaceName,
-      functionPrefix: system.namespace === "" ? "" : `${system.namespace}__`,
-      functions,
-      errors,
-      imports,
-    });
-    // write to file
-    const fullOutputPath = path.join(rootDir, outDir, systemInterfaceName + ".sol");
-    await formatAndWriteSolidity(output, fullOutputPath, "Generated system interface");
+  const systems = (await resolveSystems({ rootDir, config })).map((system) => {
+    const interfaceName = `I${system.label}`;
+    return {
+      ...system,
+      interfaceName,
+      interfacePath: path.join(path.dirname(outputPath), `${interfaceName}.sol`),
+    };
+  });
 
-    // prepare imports for IWorld
-    systemInterfaceImports.push({
-      symbol: systemInterfaceName,
-      fromPath: `${systemInterfaceName}.sol`,
-      usedInPath: "./",
-    });
-  }
+  const worldImports = systems.map(
+    (system): ImportDatum => ({
+      symbol: system.interfaceName,
+      path: "./" + path.relative(path.dirname(outputPath), system.interfacePath),
+    }),
+  );
+
+  await Promise.all(
+    systems.map(async (system) => {
+      const data = await fs.readFile(system.sourcePath, "utf8");
+      // get external functions from a contract
+      const { functions, errors, symbolImports } = contractToInterface(data, system.label);
+      const imports = symbolImports.map(
+        ({ symbol, path: importPath }): ImportDatum => ({
+          symbol,
+          path: importPath.startsWith(".")
+            ? "./" + path.relative(outDir, path.join(rootDir, path.dirname(system.sourcePath), importPath))
+            : importPath,
+        }),
+      );
+      const output = renderSystemInterface({
+        name: system.interfaceName,
+        functionPrefix: system.namespace === "" ? "" : `${system.namespace}__`,
+        functions,
+        errors,
+        imports,
+      });
+      // write to file
+      await formatAndWriteSolidity(output, system.interfacePath, "Generated system interface");
+    }),
+  );
 
   // render IWorld
   const output = renderWorldInterface({
     interfaceName: config.codegen.worldInterfaceName,
-    imports: systemInterfaceImports,
-    storeImportPath: config.codegen.storeImportPath,
-    worldImportPath: config.codegen.worldImportPath,
+    imports: worldImports,
+    storeImportPath: config.codegen.storeImportPath.startsWith(".")
+      ? "./" + path.relative(path.dirname(outputPath), path.join(rootDir, config.codegen.storeImportPath))
+      : config.codegen.storeImportPath,
+    worldImportPath: config.codegen.worldImportPath.startsWith(".")
+      ? "./" + path.relative(path.dirname(outputPath), path.join(rootDir, config.codegen.worldImportPath))
+      : config.codegen.worldImportPath,
   });
   // write to file
-  const fullOutputPath = path.join(rootDir, outDir, config.codegen.worldInterfaceName + ".sol");
-  await formatAndWriteSolidity(output, fullOutputPath, "Generated world interface");
+  await formatAndWriteSolidity(output, outputPath, "Generated world interface");
 }
diff --git a/packages/world/ts/node/resolveSystems.ts b/packages/world/ts/node/resolveSystems.ts
index 651cc9e3bf..853590fd48 100644
--- a/packages/world/ts/node/resolveSystems.ts
+++ b/packages/world/ts/node/resolveSystems.ts
@@ -2,6 +2,7 @@ import { isHex } from "viem";
 import { getSystemContracts } from "./getSystemContracts";
 import { System, World } from "../config/v2";
 import { resolveSystem } from "../config/v2/system";
+import { resolveNamespace } from "@latticexyz/store/config/v2";
 
 export type ResolvedSystem = System & {
   readonly sourcePath: string;
@@ -15,7 +16,7 @@ export async function resolveSystems({
   config: World;
 }): Promise<readonly ResolvedSystem[]> {
   const systemContracts = await getSystemContracts({ rootDir, config });
-  const contractNames = systemContracts.map((contract) => contract.name);
+  const contractNames = systemContracts.map((contract) => contract.systemLabel);
 
   // validate every system in config refers to an existing system contract
   const missingSystems = Object.keys(config.systems).filter((systemLabel) => !contractNames.includes(systemLabel));
@@ -26,7 +27,12 @@ export async function resolveSystems({
   const systems = systemContracts
     .map((contract): ResolvedSystem => {
       const systemConfig =
-        config.systems[contract.name] ?? resolveSystem({ label: contract.name, namespace: config.namespace });
+        config.systems[contract.systemLabel] ??
+        // TODO: move this to `resolveNamespace({ systems: ... })`
+        resolveSystem({
+          label: contract.systemLabel,
+          namespace: resolveNamespace({ label: contract.namespaceLabel }).namespace,
+        });
       return {
         ...systemConfig,
         sourcePath: contract.sourcePath,
diff --git a/packages/world/ts/scripts/generate-test-tables.ts b/packages/world/ts/scripts/generate-test-tables.ts
index eb382c1c82..8292cbe737 100644
--- a/packages/world/ts/scripts/generate-test-tables.ts
+++ b/packages/world/ts/scripts/generate-test-tables.ts
@@ -3,10 +3,10 @@ import { defineWorld } from "../config/v2/world";
 import { fileURLToPath } from "node:url";
 import path from "node:path";
 
-const configPath = fileURLToPath(import.meta.url);
+const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
 
 const config = defineWorld({
-  sourceDirectory: "../../test",
+  sourceDirectory: "test",
   tables: {
     Bool: {
       schema: {
@@ -40,4 +40,4 @@ const config = defineWorld({
   },
 });
 
-await tablegen({ rootDir: path.dirname(configPath), config });
+await tablegen({ rootDir, config });
diff --git a/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol b/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol
index 34615b56ae..3cec7bd18d 100644
--- a/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol
+++ b/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { IIncrementSystem } from "./IIncrementSystem.sol";
 
 /**
diff --git a/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol b/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol
index 34615b56ae..3cec7bd18d 100644
--- a/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol
+++ b/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { IIncrementSystem } from "./IIncrementSystem.sol";
 
 /**
diff --git a/templates/react/packages/contracts/src/codegen/world/IWorld.sol b/templates/react/packages/contracts/src/codegen/world/IWorld.sol
index b3848a4577..71352c40ca 100644
--- a/templates/react/packages/contracts/src/codegen/world/IWorld.sol
+++ b/templates/react/packages/contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { ITasksSystem } from "./ITasksSystem.sol";
 
 /**
diff --git a/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol b/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol
index d27f33eb9a..368699df7d 100644
--- a/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol
+++ b/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { IMoveSystem } from "./IMoveSystem.sol";
 
 /**
diff --git a/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol b/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol
index 34615b56ae..3cec7bd18d 100644
--- a/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol
+++ b/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { IIncrementSystem } from "./IIncrementSystem.sol";
 
 /**
diff --git a/test/mock-game-contracts/src/codegen/world/IWorld.sol b/test/mock-game-contracts/src/codegen/world/IWorld.sol
index d27f33eb9a..368699df7d 100644
--- a/test/mock-game-contracts/src/codegen/world/IWorld.sol
+++ b/test/mock-game-contracts/src/codegen/world/IWorld.sol
@@ -4,7 +4,6 @@ pragma solidity >=0.8.24;
 /* Autogenerated file. Do not edit manually. */
 
 import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
 import { IMoveSystem } from "./IMoveSystem.sol";
 
 /**