[TECHNICAL ARCHITECTURE REPORT / 2026]

Autonomous Developer Agents for High-Throughput 3D Environments

Aetheris Studios develops distributed multi-agent runtimes that automate procedural asset synthesis, strict Luau network architecture, and closed-loop headless simulation testing for real-time multiplayer worlds.

Intelligence layer
Claude API · Multi-Agent MCP
Target runtime
Roblox Engine · Luau
Asset bridge
Blender over MCP
Verification
Headless Studio CLI
§01 Tooling aetheris run

Three sub-agents, one verified build

The orchestrator decomposes a feature request into typed work units and dispatches them to specialized sub-agents over Model Context Protocol bridges. Nothing merges until the Judge passes every gate in a clean headless Studio session.

aetheris-orchestrator — run 7f3a2c · feature/inventory-crates
Example trace
1 Modeler · blender-mcp 2 Coder · luau 3 Judge · studio-headless
aetheris run feature/inventory-crates --place aetheris-sandbox.rbxlx --gates strict
00:00.000ORCHrun 7f3a2c accepted · plan: 3 work units · budget 900 s / 400 tool calls
00:00.014ORCHmcp connect blender-bridge transport=stdio tools=24 ready
00:00.021ORCHmcp connect studio-bridge transport=stdio tools=31 ready
00:00.030ORCHschedule WU-1 → WU-2 → WU-3 · dispatch WU-1 → modeler
00:00.118MODELERWU-1 asset crate_reinforced_v3 · budget < 4,000 tris · rig: none
00:00.204MODELER→ blender.execute_blender_code bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method="BEAUTY")
00:01.392MODELER← faces 2,184 → tris 4,368 · over budget by 368
00:01.410MODELER→ blender.execute_blender_code DECIMATE ratio=0.89 use_collapse_triangulate=True
00:02.876MODELER← tris 3,887 · non-manifold edges 0 · loose verts 0
00:02.901MODELER→ blender.execute_blender_code uv.smart_project(angle_limit=radians(66), island_margin=0.003)
00:03.540MODELER→ blender.execute_blender_code uv.pack_islands(rotate=True, margin=0.002)
00:04.117MODELER← 37 islands · overlap 0 · utilization 81.4%
00:04.380MODELER→ blender.execute_blender_code export_scene.fbx(axis_up="Y", apply_scale_options="FBX_SCALE_UNITS")
00:04.912MODELER✓ WU-1 complete · crate_reinforced_v3.fbx · 1.21 MB · sha256:9c1e…a07b
00:04.930ORCHdispatch WU-2 → coder · inputs: spec inventory-crates, asset crate_reinforced_v3
00:05.020CODERWU-2 target src/server/ProfileService.luau · languageMode strict
00:05.311CODERwrite src/server/ProfileService.luau (+84) · src/shared/Remotes.luau (+61) · src/shared/Types.luau (+38)
00:05.402CODERrojo sourcemap default.project.json -o sourcemap.json
00:05.590CODERluau-lsp analyze --sourcemap=sourcemap.json --definitions=globalTypes.d.luau src/
00:06.214CODER← 41 modules · 0 errors · 0 warnings
00:06.240CODERrojo build default.project.json -o out/place.rbxlx (rojo 7.4)
00:06.418CODER→ studio.execute_luau register remotes under ReplicatedStorage.Net
00:06.602CODER← RemoteEvent Net.ProfileLoaded server→client buffer 1,864 B
00:06.603CODER← RemoteEvent Net.InventoryMutate client→server buffer 9 B · 8/s
00:06.604CODER← RemoteFunction Net.RequestProfile client→server buffer 4 B
00:06.712CODER✓ WU-2 complete · 3 remotes registered · 0 client-writable state paths
00:06.730ORCHWU-3 unblocked → judge · clean session · place out/place.rbxlx
00:06.902JUDGEgate G1 asset topology ........... pass 3,887 / 4,000 tris · manifold · uv overlap 0
00:07.388JUDGEgate G2 typecheck --!strict ...... pass 41 modules · 0 type mismatches
00:07.401JUDGE→ studio.start_stop_play mode=server clients=2 (local test session)
00:08.430JUDGE→ studio.execute_luau TestEZ.TestBootstrap:run({ ServerStorage.Specs }) · 38 specs
00:11.913JUDGEgate G3 unit specs ............... pass 38 / 38 · 0 skipped
00:12.330JUDGEgate G4 schema fuzz .............. pass 2,000 malformed payloads · 0 accepted
00:14.008JUDGEgate G5 authority audit .......... pass 0 client-authoritative writes
00:19.552JUDGEgate G6 heap / connections ....... pass 500 join/leave cycles · +0.0 MB retained · 0 live connections
00:19.560JUDGEgate G7 persistence .............. pass session-lock contention (mocked DataStore) · no double-write
00:19.571JUDGE✓ integration gates 7/7 (100%) · memory leaks 0 · type mismatches 0
00:19.580ORCHverdict ACCEPT · merged feature/inventory-crates → main · run 7f3a2c closed in 19.58 s
ACCEPTED step 3/3 elapsed 00:19.580 gates 7/7 mcp: blender ✓ · studio ✓
src/server/ProfileService.luau--!strict · 84 lines0 diagnostics
--!strict
-- ProfileService: session-locked player persistence (server only)
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

local Types = require(script.Parent.Parent.Shared.Types)
local Validate = require(script.Parent.Validate)

type ProfileData = Types.ProfileData
type MutateRequest = Types.MutateRequest
export type Session = {
  userId: number,
  data: ProfileData,
  dirty: boolean,
}

local STORE = DataStoreService:GetDataStore("Profiles_v3")
local LOCK_TTL = 1800 -- seconds before a crashed server's lock expires
local sessions: { [Player]: Session } = {}

local ProfileService = {}

-- Write back and clear our lock, unless another server has taken it over
local function commit(userId: number, data: ProfileData)
  data.lock = nil
  pcall(function()
    STORE:UpdateAsync(`profile_{userId}`, function(old: ProfileData?): ProfileData?
      if old and old.lock and old.lock.jobId ~= game.JobId then
        return nil -- lock was taken over: never overwrite another server
      end
      return data
    end)
  end)
end

function ProfileService.load(player: Player): Session?
  local ok, result = pcall(function()
    return STORE:UpdateAsync(`profile_{player.UserId}`, function(old: ProfileData?): ProfileData?
      local data = old or Types.defaultProfile()
      local lock = data.lock
      if lock and lock.jobId ~= game.JobId and os.time() - lock.at < LOCK_TTL then
        return nil -- held by a live server: cancel without writing
      end
      data.lock = { jobId = game.JobId, at = os.time() }
      return data
    end)
  end)
  if not ok or result == nil then
    return nil
  end
  if not player:IsDescendantOf(Players) then
    commit(player.UserId, result) -- left mid-load: release the lock now
    return nil
  end
  local session: Session = { userId = player.UserId, data = result, dirty = false }
  sessions[player] = session
  return session
end

function ProfileService.mutate(player: Player, req: MutateRequest): (boolean, string?)
  local session = sessions[player]
  if session == nil then
    return false, "no_session"
  end
  local valid, reason = Validate.mutate(req, session.data)
  if not valid then
    return false, reason
  end
  local held = session.data.inventory[req.itemId] or 0
  session.data.inventory[req.itemId] = math.clamp(held + req.delta, 0, 999)
  session.dirty = true
  return true, nil
end

function ProfileService.release(player: Player)
  local session = sessions[player]
  if session == nil then
    return
  end
  sessions[player] = nil
  commit(session.userId, session.data)
end

return ProfileService
Luau.Astselect a node to locate it
node AstStatFunction lines 36–58 type (player: Player) -> Session?
Server → client6.10KB/s per player
Client → server0.47KB/s per player
Rejected payloads140.31%
Session2clients · 60 Hz
RemoteClassDirCalls/sPayloadKB/sp99 serverValidationRejectedTrend
Net.WorldStateDeltaUnreliableRemoteEventS→C20.0312 B6.090.04 msserver-origin0
Net.ClockSyncUnreliableRemoteEventS→C1.08 B0.010.01 msserver-origin0
Net.ProfileLoadedRemoteEventS→C0.01,864 B0.000.11 msserver-origin0
Net.InventoryMutateRemoteEventC→S6.09 B0.050.09 msschema · rate 8/s · ownership11
Net.InputIntentUnreliableRemoteEventC→S30.014 B0.410.03 msbounds · rate 60/s3
Net.InteractIntentRemoteEventC→S2.06 B0.010.05 msschema · range · rate 10/s0
Net.RequestProfileRemoteFunctionC→S0.04 B0.000.18 msschema · 1 in flight0
sampling 800 ms encoding buffer budget 50 KB/s per player server-authoritative · 0 client-owned state
§02 Architecture 3 subsystems · isolated sub-processes

Core subsystems

Each subsystem runs as an isolated sub-process behind a typed contract. The orchestrator owns scheduling and state; a sub-agent owns nothing it cannot hand back as a verifiable artifact.

SUBSYS-01 · topo

Procedural Topology Pipeline

Background Blender sessions compile source geometry into engine-ready meshes with no interactive viewport. Every asset is triangulated, decimated to a hard polygon budget, UV-packed and, where it needs one, rigged programmatically before export.

modeler/budget.pybpy · bmesh
BUDGET = 4000   # hard cap: tris per mesh
MARGIN = 0.97   # decimation is approximate
bm = bmesh.new()
bm.from_mesh(obj.data)
bmesh.ops.triangulate(bm, faces=bm.faces[:])
bm.to_mesh(obj.data)
bm.free()
n = len(obj.data.polygons)
if n >= BUDGET:
    m = obj.modifiers.new("cap", "DECIMATE")
    m.ratio = MARGIN * BUDGET / n
    m.use_collapse_triangulate = True
Compilation
Headless asset compilation with blender --background, driven over the MCP bridge
Decimation
Automatic, to < 4,000 polygons per mesh, with a manifold check after every pass
Rigging
Programmatic skeletal rigging: armature from a joint template, weights transferred from a proxy mesh
Export
FBX or glTF, transforms applied, Y-up, content-addressed by SHA-256
GATE G1Asset rejected on budget, manifold or UV-overlap failure.
SUBSYS-02 · net

Strict-Type Luau Networking

Network code is generated and checked under --!strict. Remotes are declared from one schema module, so client and server share a single definition of every payload, and the server stays the only authority over persistent state.

client/Inventory.luau9-byte intent
-- one buffer per remote, reused on
-- every call: no per-call allocation
local OUT = buffer.create(9)

local function mutate(op: number,
    item: number, delta: number,
    seq: number)
  buffer.writeu8(OUT, 0, op)
  buffer.writeu16(OUT, 1, item)
  buffer.writei16(OUT, 3, delta)
  buffer.writeu32(OUT, 5, seq)
  Net.InventoryMutate:FireServer(OUT)
end
Replication
Zero-allocation remote replication: fixed-layout buffer payloads written into pre-allocated scratch buffers
Authority
Client-server authority reconciliation: clients send intents, the server applies them and replicates the result
Sandboxing
Exploit surface sandboxing: schema, rate and ownership checks on every client-to-server call
Typecheck
luau-lsp analyze, languageMode strict, blocking in CI
GATES G4–G5Build fails on any accepted malformed payload or client-writable state path.
SUBSYS-03 · verify

Closed-Loop Runtime Verification

A Judge agent drives a clean headless Studio session through the Model Context Protocol, runs specs and stress scenarios, and routes structured tracebacks back to the Coder. The loop repeats until every gate passes or the run budget is spent.

judge → studio-bridgeJSON-RPC 2.0
{
  "jsonrpc": "2.0",
  "id": 41,
  "method": "tools/call",
  "params": {
    "name": "execute_luau",
    "arguments": {
      "code": "return Gates.run('G6.heap')"
    }
  }
}
CI transport
Continuous integration via Model Context Protocol: JSON-RPC 2.0 over stdio to a Studio plugin bridge
Stress
Automated stress-testing: simulated clients, join/leave soak and payload fuzzing at configurable tick rates
Patching
Automated traceback patching: stack frame → source map → minimal diff → re-verify
Verdict
ACCEPT or REJECT, with the full gate log attached to the run
GATES G2–G7No merge without a passing verdict from a clean session.
fig. 1 — orchestration topology dispatch / artifactsverification feedback
Orchestration topology A feature request enters the orchestrator, which dispatches work units to the Modeler, Coder and Judge. The Modeler drives Blender over MCP, the Coder writes the source tree and builds with Rojo, and the Judge drives a headless Roblox Studio session over MCP. Judge verdicts and tracebacks return to the orchestrator. Feature request spec · constraints Orchestrator plan → typed work units schedule · budgets · grants gate verdicts run log (append-only) dispatch Modeler WU · asset synthesis Coder WU · Luau + remotes Judge WU · verification gates MCP · stdio fs · CLI MCP · stdio Blender · background session execute_blender_code · bpy · bmesh src/ · Rojo 7.4 luau-lsp analyze · rojo build Roblox Studio · headless execute_luau · start_stop_play asset.fbx out/place.rbxlx verdict · structured traceback → patch request
§03 Telemetry release 2.4.1

Engineering benchmarks

Headline figures for the current pipeline release. Each figure is defined precisely so it can be compared like for like.

BENCH · pipeline v2.4.1 profile: strict runtime: Roblox / Luau gate definitions →
99.8%
Typecheck Accuracy
--!strict

Share of generated Luau modules that pass strict-mode analysis on first submission, before any patch cycle. Nothing merges with a type error (gate G2).

<12ms
Pipeline Sync Latency
Rojo v7.4

Time from a file-system write in the source tree to the matching DataModel update in Studio during live sync.

0
Client Authoritative Vectors
Zero-Trust Model

Client-to-server paths able to write persistent or authoritative state. Every remote carries intents only.

languageMode strict sync rojo serve authority server polygon budget < 4,000 tris agent transport MCP · stdio
§04 Runtime Protocol spec rev 2.4

Runtime protocol specification

Agents never call each other. Every unit of work moves through the orchestrator as a typed envelope, and every state transition is appended to the run log. The reference transport is JSON-RPC 2.0 over stdio, the same framing the Model Context Protocol uses for local servers.

Envelope invariants

  1. I-1
    Typed inputs, content-addressed outputs. A work unit names its role, inputs, dependencies, tool grants, budget and the gates it must pass. Every artifact it returns is identified by SHA-256, so a verdict always refers to exact bytes.
  2. I-2
    Least privilege per role. Tool access is granted per unit at dispatch. The Modeler cannot write source, the Coder cannot start a play session, and the Judge writes nothing except its verdict.
  3. I-3
    Verdicts come from clean sessions. The Judge never reuses a session an author has touched. Every candidate is verified from out/place.rbxlx in a fresh headless instance.
  4. I-4
    Every run is replayable. Envelopes, tool calls and gate results are appended to an immutable log keyed by run id, so any run can be replayed against identical inputs for audit or regression.
{
  "jsonrpc": "2.0",
  "id": "wu-2:7f3a2c",
  "method": "workunit/dispatch",
  "params": {
    "run": "7f3a2c",
    "unit": "WU-2",
    "role": "coder",
    "dependsOn": ["WU-1"],
    "inputs": {
      "spec": "inventory-crates",
      "assets": ["sha256:9c1e…a07b"],
      "constraints": { "languageMode": "strict", "maxRemotes": 8 }
    },
    "grants": ["fs:src/**", "cli:rojo", "cli:luau-lsp", "mcp:studio.execute_luau"],
    "budget": { "wallclockMs": 300000, "toolCalls": 120 },
    "gates": ["G2.typecheck", "G3.specs", "G4.schema-fuzz", "G5.authority"]
  }
}
{
  "jsonrpc": "2.0",
  "id": "wu-2:7f3a2c",
  "result": {
    "status": "complete",
    "artifacts": [
      { "path": "src/server/ProfileService.luau", "sha256": "4be1…c9d0" },
      { "path": "src/shared/Remotes.luau", "sha256": "77a0…1f3e" },
      { "path": "src/shared/Types.luau", "sha256": "e52c…08b4" }
    ],
    "remotes": 3,
    "diagnostics": { "errors": 0, "warnings": 0 },
    "toolCalls": 14,
    "elapsedMs": 1782
  }
}
{
  "jsonrpc": "2.0",
  "method": "judge/verdict",
  "params": {
    "run": "7f3a2c",
    "verdict": "ACCEPT",
    "session": "studio-headless:clean",
    "gates": [
      { "id": "G1.assets", "status": "pass", "detail": "3887/4000 tris, manifold, uv overlap 0" },
      { "id": "G2.typecheck", "status": "pass", "detail": "41 modules, 0 errors" },
      { "id": "G3.specs", "status": "pass", "detail": "38/38" },
      { "id": "G4.schema-fuzz", "status": "pass", "detail": "2000 payloads, 0 accepted" },
      { "id": "G5.authority", "status": "pass", "detail": "0 client-authoritative writes" },
      { "id": "G6.heap", "status": "pass", "detail": "500 cycles, +0.0 MB, 0 live connections" },
      { "id": "G7.persistence", "status": "pass", "detail": "no double-write under lock contention" }
    ],
    "elapsedMs": 12841
  }
}

Work-unit lifecycle

fig. 2
Work-unit lifecycle Queued, dispatched, running, submitted, verifying, accepted, merged. A failed gate moves a unit from verifying to rejected, then to patching and back to submitted. A rejected unit whose budget is spent is closed. QUEUED DISPATCHED RUNNING SUBMITTED VERIFYING ACCEPTED MERGED PATCHING REJECTED CLOSED traceback budget spent gate fail resubmit

Verification gates

ordered cheapest-first
GateNameCheckEnvironmentRejects when
G1Asset topologyTriangle count, manifold geometry and UV island overlap for every new or changed meshBlender, background session4,000 or more triangles, any non-manifold edge, any UV overlap
G2Typecheckluau-lsp analyze with Roblox definitions and a Rojo sourcemap, languageMode strictCI containerAny type error
G3Unit specsTestEZ specs for every touched module, executed in the server contextHeadless StudioAny failed or skipped spec
G4Schema fuzzMalformed, truncated, oversized and out-of-range payloads sent to every client-to-server remoteHeadless Studio, 2 simulated clientsAny payload accepted by a handler
G5Authority auditStatic scan of remote handlers plus runtime tracing of writes to persistent and replicated stateCI and headless StudioAny client-originated path that writes authoritative state
G6Heap & connectionsJoin/leave soak; retained heap and live RBXScriptConnection count measured after teardownHeadless StudioRetained heap growth or any live connection after teardown
G7PersistenceSession-lock contention between two sessions against a mocked DataStoreHeadless StudioDouble-write, lost update or lock bypass
Retries

Bounded patch cycles

A rejected unit returns to the role that produced it with the failing gate's traceback, the source-mapped file and line, and the files it may change. Units get three patch cycles by default; the limit is set per run.

Budgets

Wall-clock and tool-call limits

Every run carries both limits. Exhausting either closes the run as REJECTED; partial artifacts are discarded, never merged.

Run log

Append-only, monotonic

Envelopes, tool calls and gate results are written in order with monotonic timestamps. The Execution Stream in §01 is rendered from this log format.

§05 Documentation whitepaper rev 2.4.1

Technical Whitepaper

Verified agent pipelines for multiplayer Luau

Aetheris Studiosrev 2.4.120266 sections

1Abstract

Real-time multiplayer worlds on the Roblox engine combine three failure-prone disciplines: content production, network code and runtime behavior under load. Aetheris treats all three as a single build pipeline. A feature request is decomposed into typed work units, executed by specialized sub-agents against real tools (Blender, the Luau toolchain and Roblox Studio) through Model Context Protocol bridges, and accepted only when a clean headless simulation passes a fixed set of gates. This paper describes the agent roles, the protocol that connects them and the verification model that decides what ships.

2Why generated game code fails in production

Code that compiles is not code that survives a live server. The costly failures in multiplayer Luau are structural rather than syntactic: remote handlers that trust client-supplied values, state mutated on both sides of the network boundary, connections never disconnected when a player leaves, and DataStore writes that race across servers. None of them appear in a single-player test or a successful build. A pipeline that only checks whether code runs will accept all four.

Design consequence. Verification has to exercise the network boundary, the player lifecycle and persistence explicitly, so each of those failure classes has a dedicated gate (G4 to G7).

3Agent roles and isolation

The Modeler, Coder and Judge run as separate sub-processes with separate tool grants. The Modeler can execute Python inside a background Blender session but cannot touch the source tree. The Coder can write to src/ and run the Luau toolchain but cannot start a play session. The Judge can drive Studio and read everything, and writes nothing except its verdict.

Narrow grants keep failures attributable. When a gate fails, exactly one role owns the fix, and the orchestrator knows which one without inspecting the code.

4Networking: intents, not values

Clients never send state; they send intents. "Move item 14 into slot 3" is an intent; "my inventory is now X" is a value the server must never accept. The server validates each intent against schema, rate and ownership rules, applies it to its own copy of the state and replicates the result.

Payloads use fixed binary layouts written into pre-allocated buffer objects, one per remote. That removes per-call table allocation from the hot path and gives every remote a wire format that can be enumerated, fuzzed and audited mechanically.

5Verification model

The Judge verifies every candidate build in a fresh headless Studio session. Gates run cheapest-first, so most rejections cost seconds rather than minutes: asset topology and static typecheck, then unit specs, then schema fuzzing and the authority audit, then a join/leave soak that measures retained heap and live connections, and finally session-lock contention against a mocked DataStore.

A failing gate emits a structured traceback (gate id, stack, source-mapped file and line), which the orchestrator routes to the responsible role together with the list of files it is allowed to change.

6Scope and limitations

The pipeline targets server-authoritative experiences with Rojo-managed source trees. Client-owned physics, such as vehicles or custom character controllers, is verified for safety but not for feel; that still needs human playtesting. Asset synthesis covers hard-surface props and modular environment pieces, while organic characters are rigged from joint templates rather than generated.

Gates establish the absence of the failure modes they test for, not the absence of all defects. Every merge remains reviewable by a human engineer.

§06 Developer Portal API access · contact

API access and engineering contact

Protocol questions, integration discussions and partnership requests go directly to the founder, with no ticket queue and no sales handoff. A useful first message covers:

  • ExperienceGenre, target concurrency per server and current live status
  • Source layoutRojo-managed repository or Studio-only place files
  • SubsystemTopology pipeline, Luau networking or runtime verification
  • ConstraintsExisting frameworks, platform policies and delivery dates
ENGINEERING CONTACTaetherisstudios.dev
Entity
Aetheris Studios
Domain
aetherisstudios.dev
Scope
Multi-agent developer tooling and simulation runtimes for real-time 3D