Add a template asset to the marketplace

How to add a template asset to the marketplace

This guide explains exactly how to structure your asset zip so SuperbulletAI can import it reliably and without manual fixes.

1. Zip layout (contract)

TemplateSystem.zip
├─ Instructions.md               # consumed by Instruction Runner
├─ metadata.json                 # base system metadata (version_number, created_by)
├─ relevant_systems.json         # bundled systems index: [{ display_name, unique_id, always_on, isInitiallyChecked }]
├─ Unpack_On_Source/             # copy-merge into src/... (text-based; .rbxm/.rbxmx allowed sparingly)
├─ Unpack_On_Roblox_Studio/      # DataModel-bound assets (models/UI/VFX) as .rbxm/.rbxmx
└─ To_Upload_By_User/            # assets that must be uploaded by the user (Animations/Sounds)

2. Folder rules and best practices

Both Unpack_On_Source/ and Unpack_On_Roblox_Studio/ are treated like a src/ root. Use Roblox service names as top‑level folders to indicate destinations (e.g., ReplicatedStorage/, ServerScriptService/, StarterGui/, Workspace/).

a. Unpack_On_Source/

  • Purpose: code and other text‑based assets that can be merged into source control.
  • Do: use the project's real source roots — client code under ReplicatedStorage/ClientSource/Client/, server code under ServerScriptService/ServerSource/Server/, shared code and data under ReplicatedStorage/SharedSource/.
  • Do: keep names stable and deterministic for idempotent merges.
  • Allowed, but sparingly: .rbxm/.rbxmx files. Prefer Unpack_On_Roblox_Studio/ for DataModel-bound assets; only place them here when there is a specific reason to.
  • Do not: include runtime‑only assets that belong in the DataModel (UI/VFX/meshes).
  • Do not: invent your own roots (ReplicatedStorage/Systems/…, ServerScriptService/Systems/…). Anything outside the real roots is not picked up by the Knit loader.

Controllers and Services are folder clones, not single .lua files

A new controller is a copy of TemplateController, renamed — never a flat MySystemController.lua. Same on the server: a new service is a copy of TemplateService. The template ships the folder shape the components initializer expects, so cloning it is what makes the system load at all.

Clone this (from src/ReplicatedStorage/ClientSource/Client/TemplateController/):

<Name>Controller/
├─ init.lua                    # Knit.CreateController({ Name = "<Name>Controller", Instance = script })
└─ Components/
   ├─ init.meta.json           # { "ignoreUnknownInstances": true }
   ├─ Get().lua                # module with .Init() / .Start()
   ├─ Set().lua                # module with .Init() / .Start()
   └─ Others/
      ├─ init.meta.json        # { "ignoreUnknownInstances": true }
      └─ Template.lua          # copy + rename this for each extra component module

Rules when cloning:

  • Rename the folder and the Name field in init.lua together — they must match (Name = "<Name>Controller").
  • Keep Instance = script in init.lua. That is what auto-initializes everything under Components/.
  • Keep both init.meta.json files. Dropping them breaks Rojo/Studio round-tripping of the folder.
  • Add your own component modules under Components/Others/ by copying Template.lua and renaming it — keep its .Init() / .Start() signature.
  • TemplateController and TemplateService themselves are guarded project files. Ship your renamed clone; never ship a modified TemplateController.

Example

Unpack_On_Source/
  ReplicatedStorage/
    ClientSource/
      Client/
        InventoryController/            # clone of TemplateController
          init.lua
          Components/
            init.meta.json
            Get().lua
            Set().lua
            Others/
              init.meta.json
              Template.lua
              SlotRenderer.lua          # your component (copied from Template.lua)
    SharedSource/
      Datas/
        Inventory/
          ItemDefinitions.lua
  ServerScriptService/
    ServerSource/
      Server/
        InventoryService/               # clone of TemplateService
          init.lua
          Components/
            init.meta.json
            Get().lua
            Set().lua
            Others/
              init.meta.json
              Template.lua

b. Unpack_On_Roblox_Studio/

  • Purpose: non‑script assets destined for the DataModel.
  • Do: package assets as .rbxm/.rbxmx (UI, VFX, models, folders of Parts, etc.).
  • Do: place under service folders to indicate where they go.
  • Do not: include any Scripts/LocalScripts/ModuleScripts here.

.rbxm file rules

  • One unique instance name per .rbxm: Each .rbxm file must contain only one top-level instance with a unique name.
  • Avoid duplicate children names: Duplicate children names within an .rbxm are not properly supported and may result in instance loss when exported from Superbullet to Roblox Studio.
  • File name must match instance name exactly: The .rbxm filename must match the exact name of the instance in Roblox Studio (e.g., an instance named Example_Model in Studio should be saved as Example_Model.rbxm).

Example

Unpack_On_Roblox_Studio/
  StarterGui/
    InventoryUI.rbxmx
  ReplicatedStorage/
    VFX/
      ItemPickupVFX.rbxm

c. To_Upload_By_User/

  • Purpose: assets that must be uploaded under the developer’s account so the importer can reference their asset IDs.
  • Structure:
    • To_Upload_By_User/Animations/ — contains only Animation instances
    • To_Upload_By_User/Sounds/ — contains only Sound instances
  • Do: include pure instances only (no scripts, no extra folders beyond the two above).
  • Do: give descriptive names (e.g., SwordSwing, BackpackOpen), one instance per file if you export models.
  • Do not: mix sounds and animations in the same folder.

Example

To_Upload_By_User/
  Animations/
    SwordSwing
    PotionDrink
  Sounds/
    SwordSwingSFX
    BackpackOpenSFX

3. relevant_systems.json (bundled systems you can import together)

Purpose

  • Lists other systems packaged alongside the current system that the user may want to import together.
  • Triggers a "Relevant systems" modal so users can pick which to include.
  • Lives at the zip root next to metadata.json.

Schema (array of entries)

  • display_name (string): User‑facing name.
  • unique_id (string): Globally unique ID of the system (from marketplace registry).
  • always_on (boolean): If true, cannot be unchecked in the modal and is installed first if missing.
  • isInitiallyChecked (boolean): If true, checked by default (unless always_on true).

Example

[
  {
    "display_name": "Inventory Core",
    "unique_id": "a2b4c6d8-e0f2-4a6c-9b8d-1234567890ab",
    "always_on": true,
    "isInitiallyChecked": true
  },
  {
    "display_name": "Economy Basics",
    "unique_id": "c0ffee00-0000-4000-8000-0000c0ffee00",
    "always_on": false,
    "isInitiallyChecked": true
  },
  {
    "display_name": "Vehicles Extension",
    "unique_id": "deadbeef-dead-beef-dead-beefdeadbeef",
    "always_on": false,
    "isInitiallyChecked": false
  }
]

Guidelines

  • Use unique_id only; do not include file paths or download URLs.
  • Keep the list short and relevant.
  • Mark required dependencies with always_on: true. The app installs these first if missing and then proceeds.

Learn more: see the dedicated page "relevant_systems.json" for full contract and behaviors.

4. metadata.json (zip root)

{
  "version_number": "1.0.0",
  "created_by": "Superbullet"
}
  • Use semantic versioning for version_number (e.g., 1.0.1, 1.1.0, 2.0.0).
  • Bump version_number on every re‑upload so templates update for everyone.
  • created_by is set automatically by the uploader.

5. Instructions.md (how to write high‑quality instructions)

What is the Instruction Runner?

The Instruction Runner is the part of SuperbulletAI that delivers your Instructions.md to the AI after your system is unpacked. It reads the file (including any subsystem Instructions.md files) and sends the raw markdown as a normal user message — no wrapper, no special format. The AI then applies the described edits using its file‑edit tools. If your markdown includes a structured "Steps" section, the AI treats it as the source of truth; otherwise it derives steps from the prose.

In short: the Instruction Runner doesn't execute anything itself — your Instructions.md is read verbatim by the AI, so write it as if you were messaging the AI directly.

Writing guidelines

Write concise, deterministic steps so the AI can make idempotent changes. Prefer imperative, testable language.

Do

  • State the goal up front.
  • Reference exact locations by service and path.
  • Specify concrete values and types.
  • Include acceptance checks the AI can verify.

Avoid

  • Vague language ("tweak", "adjust").
  • Hidden manual steps.
  • Multi‑goal instructions in one paragraph.

Minimal example

Goal: Add a coins counter to player profiles.

1) In ServerScriptService/ServerSource/Server/ProfileService.lua, add a new number field to the profile template:
   Coins = 0

2) In StarterGui/PlayerHUD, ensure a TextLabel named CoinsLabel exists.

3) In ReplicatedStorage/ClientSource/Client/CurrencyController/Components/Get().lua, update the render loop to display Coins on CoinsLabel.

Acceptance:
- A new player starts with Coins == 0.
- CoinsLabel shows "0" on spawn.

6. Common mistakes (avoid these)

  • Shipping a controller or service as a single flat .lua file (e.g., InventoryController.lua) instead of a renamed clone of the TemplateController/TemplateService folder.
  • Putting client/server code under invented roots like ReplicatedStorage/Systems/… instead of ClientSource/Client/… and ServerSource/Server/….
  • Dropping the Components/init.meta.json or Components/Others/init.meta.json files when cloning the template.
  • Renaming the controller folder but leaving the old Name in init.lua (or vice versa).
  • Overusing .rbxm/.rbxmx files inside Unpack_On_Source/ (allowed, but use sparingly — prefer Unpack_On_Roblox_Studio/).
  • Including any Scripts/LocalScripts/ModuleScripts inside Unpack_On_Roblox_Studio/.
  • Not using service folders at the top level (e.g., dropping files directly under root).
  • Mixing animations and sounds in the same folder under To_Upload_By_User/.
  • Forgetting to bump version_number in metadata.json when re‑uploading.
  • Using duplicate children names in .rbxm files (causes instance loss on export).
  • Mismatching .rbxm filenames with the instance name in Roblox Studio.
  • Having multiple top-level instances in a single .rbxm file.

7. Pre‑upload checklist

  • The zip matches the layout above and uses service folders correctly.
  • Every new controller/service is a renamed clone of TemplateController/TemplateService (folder with init.lua + Components/), not a flat .lua file, and its folder name matches the Name in init.lua.
  • Client code lives under ReplicatedStorage/ClientSource/Client/, server code under ServerScriptService/ServerSource/Server/, shared data under ReplicatedStorage/SharedSource/.
  • .rbxm/.rbxmx in Unpack_On_Source/ are used only sparingly (prefer Unpack_On_Roblox_Studio/); no scripts in Unpack_On_Roblox_Studio/.
  • Animations and sounds are pure instances in the right folders.
  • relevant_systems.json lists only truly related systems with valid unique_id values.
  • metadata.json has a bumped semantic version_number.
  • Instructions.md is precise, testable, and idempotent.
  • relevant_systems.json entries reference valid unique_id values; use always_on for required dependencies when applicable.