Skip to content

Assembly Asset Authoring Workflow

This is the current lightweight workflow for turning an assembly-v1 .rbxm asset plus vehicle-developer product input into vehicle-definition and dealership-package data. It is not fully automated yet, but the inspection output is meant to make the hand-authored files more mechanical.

The intended direction is standardized vehicle-developer input: trims/packages, VehicleIds, prices, images, display names, paints, rims, base assembly, accessories, and tuning should be provided in an easy repeatable shape. A generator can then produce source files from that input. If the standardized input omits a product value, the generated draft may use a placeholder such as 0, "", or a temporary id, but those placeholders should be visible review items rather than hidden assumptions.

Development direction

The long-term direction is for more of the vehicle-definition surface to become generated source code produced by vehicle-developer tooling:

  • VehicleDef/Vehicles/<vehicle>/init.luau
  • assembly_definitions.luau
  • accessory_definitions.luau
  • tuning_definitions.luau
  • DealershipPackageGroups/<vehicle_key>.luau

That is the reason the repository's docs/vehicles/case-studies/ folder keeps historical agent-assisted migrations and asset-specific notes. Each migration records which source inputs existed, which details had to be inferred from the RBXM, which product fields were missing, and which placeholder values remained. Those notes are not meant to be permanent manual rituals; they are training data for the workflow we want to standardize.

Because these files are moving toward generated output, prefer denormalized vehicle-def code. Repeated trims, package entries, assembly definitions, accessory definitions, and customization item lists should usually be written inline rather than hidden behind shared helper tables or clever local factory functions. A little local structure is fine when it matches an existing authored pattern, but the default should be boring, explicit data. Explicit repeated data is easier for generators to emit, easier for vehicle developers to review, and easier for agents to compare against the authored asset and product input.

In other words: optimize these files for mechanical generation and review, not for hand-written abstraction elegance.

Inspect the asset

Use the RBXM helpers before writing definitions:

mise exec -- lune run .lune/demos/rbxm/inspectModel.luau "src/ServerStorage/LocalVehicles/4444.rbxm"
mise exec -- lune run .lune/demos/rbxm/inspectScriptSources.luau "src/ServerStorage/LocalVehicles/4444.rbxm" --full
mise exec -- lune run .lune/demos/rbxm/inspectAssemblyVehicleAsset.luau "src/ServerStorage/LocalVehicles/4444.rbxm"
mise exec -- lune run .lune/verifyVehicleAssemblyAsset.luau "src/ServerStorage/LocalVehicles/4444.rbxm"

inspectAssemblyVehicleAsset.luau summarizes the asset paths that usually seed:

  • assembly_definitions.luau entries from assembly/<slot>/<model>
  • accessory_definitions.luau entries from accessory/<model>
  • tuning_definitions.luau entries from nested models under tuning/

Generated definition ids commonly start equal to these paths, but ids and paths are different concepts. The id is stable package/saved-data identity; path is the movable address under the RBXM asset.

It also lists DriveSeat paths and tags. Assembly runtime code expects the assembled output to have exactly one VehicleAssembly__DriveSeat tag and at least one VehicleAssembly__Wheel tag.

verifyVehicleAssemblyAsset.luau is the publish gate for asset shape. It also validates the assembly-v1 folding-mirror contract documented in vehicle-assets.md: explicit moving-model and pivot tags; per-model degree/speed configuration; no authored anchors; and no legacy mirror runtime artifacts.

Required LocalVehicles RBXM hierarchy

An assembly-v1 file under ServerStorage/LocalVehicles must use this exact outer shape:

Model "<asset-id>"
└── Folder "<vehicle-asset-name>"
    ├── assembly
    ├── accessory
    └── tuning

The serialized RBXM root is the Model container, not the vehicle asset Folder itself. VehicleModelManager clones the LocalVehicles entry and passes its first child to the assembly builder. Serializing the asset Folder as the root makes the manager pass that Folder's first child—often assembly—as the asset, which then fails because it has no nested assembly folder. Keep exactly one vehicle asset Folder inside the Model container, and name the outer Model after the local pseudo-asset id or published asset id.

To run the asset-shape, asset/VehicleDef contract, and package-group/VehicleDef contract validators across every assembly vehicle whose asset is currently maintained in LocalVehicles, use:

mise run validate_assembly_vehicles

The reviewed vehicle list is intentionally hard-coded in .lune/vehicleValidation/assemblyVehicles.luau until the repository contains the complete assembly-vehicle asset set. The command prints individual failure diagnostics followed by check and vehicle totals.

The package contract follows each base_accessories installation recursively. At the first level, its key must name a socket on the package's selected assembly part. At every nested level, the key must name a socket exposed by the selected parent accessory, and the installed part must be one of that socket's declared options. For example, a roof-rack:plastic accessory mounted to a frame's roof-rack socket hosts roof-rack:crossbars through its own crossbars socket—not another roof-rack socket.

Generate from a multi-vehicle authoring bundle

An authoring bundle may contain shared product metadata plus several vehicle containers instead of one publishable asset root. Treat that file as generator input, not as a LocalVehicles asset: the publish validator requires each output RBXM to deserialize to exactly one outer Model containing exactly one vehicle asset Folder.

The checked-in Delton bundle preparer is a concrete, repeatable example:

mise exec -- lune run .lune/prepareNewDeltonAssemblyVehicles.luau `
  "C:\Users\conta\Desktop\new_delton_vehicles.rbxm" `
  ".tmp/new_delton_vehicles"

It writes drafts rather than editing runtime source in place:

  • assets/<asset-id>.rbxm for each split and normalized vehicle asset;
  • vehicles/<vehicle-key>/ with the initializer and three definition modules;
  • package_groups/<vehicle-key>.luau;
  • factory-paint and factory-rim entry snippets;
  • summary.md recording the applied product decisions and output counts.

The script also demonstrates how to keep repeatable asset corrections at the generation boundary: move misplaced assembly sockets, add inferable mount attachments, normalize stable path names, remove legacy executable runtime state, add assembly tags, and convert old power-folding mirror scripts into the tag-and-attribute contract. Every correction is derived from authored geometry, legacy configuration, or an explicit product decision; the generator should fail when it cannot make that derivation safely.

The repository's docs/vehicles/case-studies/delton-holsman-family-assembly-generation.md records the input precedence rules, collision resolution, inferred Landslide Fleet package, and validation results from this bundle.

Author the vehicle

For a new assembly-v1 vehicle:

  1. Set AssemblySystem = "assembly-v1" and point AssetId at the local or published assembly asset.
  2. Set RootAssemblySlot to the root physical slot. Define each reachable child slot in the selected part's assembly_sockets.
  3. Put sibling definition modules next to the vehicle init.luau: assembly_definitions.luau, accessory_definitions.luau, and, if needed, tuning_definitions.luau.
  4. Author a matching DealershipPackageGroups/<vehicle_key>.luau when packages need to expose choices that are not one-package-per-trim.
  5. Make each package's vehicle_id explicit. It is the purchased trim identity, not something package purchase derives from the final assembly.

Package-group display names

Set a package group's display_name to "" when the vehicle has only one group. The vehicle and package names already provide the useful labels, so a group-level label would only repeat them. Give groups display names only when a vehicle exposes multiple groups and the UI must distinguish their purposes, such as Sierra HT's Stock, Tow, Delivery, and Stakebed groups. This convention applies to generated package groups as well as hand-authored ones.

For the 2026 Sternauster-Mullerline Standhaft, the initial tree is frame -> body. The asset also has a drivetrain-badge assembly socket, but there is no base "no badge" part yet, so the first package setup keeps V12 as a tuning overlay only.