Dustwright

Authoring Dustwright designs

A guide for LLM agents (and people) writing redstone designs in the rsd/1 format for Minecraft Bedrock Edition, or Java Edition (see Java Edition). Read it once, then work in the loop at the end: write, validate, fix.

The format in one minute

A design is one JSON file, <name>.rsd.json: horizontal layers of character rows, and a legend that maps each character to a block.

{
  "format": "rsd/1",
  "meta": { "name": "NOT gate", "edition": "bedrock", "gameVersion": "1.26.50" },
  "legend": { "#": "stone", "L": "lever mount=floor", "-": "dust", "t": "torch mount=west", "*": "lamp" },
  "layers": [["#####"], ["L-#t*"]],
  "ports": {
    "inputs": [{ "name": "A", "pos": [0, 1, 0] }],
    "outputs": [{ "name": "out", "pos": [4, 1, 0] }]
  },
  "tests": [
    { "set": { "A": 0 }, "afterTicks": 4, "expect": { "out": 1 } },
    { "set": { "A": 1 }, "afterTicks": 4, "expect": { "out": 0 } }
  ]
}

Required keys: format (always "rsd/1"), meta.name, legend, layers. Unknown keys are errors; put tool-specific data under ext.

Axes and grid

Block specs

<type> [key=value ...] with single spaces, all lowercase: repeater facing=east delay=2. Values are lowercase words, integers or true/false. Unspecified keys take the default, but always write orientation keys explicitly. A spec is at most 200 characters.

Orientation (identical in the viewer, validator and every exporter)

Type Key Meaning
repeater, comparator facing Direction the signal exits
torch, lever, button mount Where the supporting block is: floor, ceiling, or north/south/east/west
piston, sticky_piston facing Direction the head pushes
observer facing Direction the face looks (its input side); the output is the opposite side
hopper, dropper, dispenser facing Direction items leave
stairs facing, half Direction you walk to climb them (the tall back is on that side); half=top is upside down
slab half bottom or top half of the cell
door facing Direction a player looks when placing it; hinge is left or right
trapdoor facing, half The hinge is on the edge opposite facing; half is the bottom or top of the cell when closed

Examples: a torch on the east face of a block that sits west of it is torch mount=west. A repeater carrying a signal from west to east is repeater facing=east. Torches have no ceiling mount; levers and buttons do.

Block list

Type Category States (default) Needs support Port
button power source mount floor/ceiling/north/south/east/west (default floor); material stone/wood (default stone); pressed false/true (default false) by mount input
comparator component facing north/south/east/west (default north); mode compare/subtract (default compare); powered false/true (default false) below output (0..15)
concrete solid color 16 dye colours, e.g. white/red/light_blue (default white) — —
daylight_sensor power source inverted false/true (default false) — —
dispenser consumer facing down/up/north/south/east/west (default north) — —
door consumer facing north/south/east/west (default north); hinge left/right (default left); open false/true (default false); material wood/iron (default wood) below output (0/1)
dropper consumer facing down/up/north/south/east/west (default north) — —
dust component power 0..15 (default 0) below output (0..15)
glass transparent — — —
honey_block transparent — — —
hopper consumer facing down/north/south/east/west (default down) — —
lamp consumer lit false/true (default false) — output (0/1)
lever power source mount floor/ceiling/north/south/east/west (default floor); on false/true (default false) by mount input
note_block solid — — output (0/1)
observer component facing down/up/north/south/east/west (default north); powered false/true (default false) — —
piston consumer facing down/up/north/south/east/west (default up) — output (0/1)
pressure_plate power source material stone/wood (default stone); on false/true (default false) below input
redstone_block power source — — —
repeater component facing north/south/east/west (default north); delay 1/2/3/4 (default 1); powered false/true (default false) below —
slab transparent half bottom/top (default bottom) — —
slime transparent — — —
stairs transparent facing north/south/east/west (default north); half bottom/top (default bottom) — —
sticky_piston consumer facing down/up/north/south/east/west (default up) — output (0/1)
stone solid — — —
target solid — — —
torch power source mount floor/north/south/east/west (default floor); lit false/true (default true) by mount —
trapdoor consumer facing north/south/east/west (default north); half bottom/top (default bottom); open false/true (default false); material wood/iron (default wood) — output (0/1)
wool solid color 16 dye colours, e.g. white/red/light_blue (default white) — —

"Needs support" blocks must sit on a block that holds them (below), or on the block their mount names (E006). Measured on Bedrock: full blocks (stone, wool, concrete, lamps, pistons, observers, ...) and glass hold on every face; honey, hoppers and top-half slabs, stairs and trapdoors hold only on top; bottom-half slabs, stairs and closed trapdoors, and daylight sensors, hold only underneath; open trapdoors hold nothing. Levers attach to any block (Bedrock; Java differs, see below). Inputs are what a test can drive (set); outputs are what it can read (expect), with the values they report.

Optional sections

Tests: how they run

Bedrock rules that bite

Java Edition

Set "edition": "java" (and "gameVersion": "26.3"). The format, orientation and block list are the same; the rules differ:

Limits

2 MB file; JSON nesting depth 8; bounding box 64 × 384 × 64 (E007); 90 legend entries; 50,000 non-air blocks; 10,000 blocks overrides; 64 inputs and 64 outputs; 64 tests; 500 annotations of at most 500 characters; 384 steps. Exceeding a limit is E013.

The loop

  1. Brief: restate the job as named inputs, outputs and a truth table; write ports and tests first.
  2. Draft: write layers and legend. Spread the circuit out; compact layouts come later.
  3. Check: rsd validate design.rsd.json --json. It prints { "ok": bool, "diagnostics": [...] } and exits 0 (ok, warnings allowed), 1 (errors) or 2 (usage or file problem). Through MCP (rsd mcp), the validate tool returns the same JSON and simulate shows each test's actual outputs.
  4. Fix: each diagnostic has a code, a pos [x,y,z] (or a JSON Pointer path), a message and a hint naming the edit. Apply it and go to 3. Stop after 6 rounds and report what still fails. Never edit ports or tests to make a failure go away; if the brief was wrong, say so.
  5. Show: rsd bundle design.rsd.json -o design.html writes one self-contained HTML viewer (publish it as an artifact or open it offline). With no tools at all, write a share link (see "Share link by hand"). rsd sheets design.rsd.json -o sheets/ writes printable A4 layer SVGs.
  6. Export: rsd export design.rsd.json --to mcfunction --pack <pack-dir> [--clear] writes functions/rsd/<slug>.mcfunction. In game, stand at the build origin and run /function rsd/<slug>; the build lands one block east. --clear also writes rsd/<slug>_clear, which empties the bounding box. Run the clear function before rebuilding in the same place: building over a build pushes moving parts again, and leaves dust that is powered at rest at signal 0 until something changes. --to mcstructure writes structures/rsd/<slug>.mcstructure instead; in game, /structure load rsd:<slug> ~1 ~ ~ (add 90_degrees and so on to turn it). Use it for builds over 10,000 blocks (E014). rsd pack design.rsd.json -o <slug>.mcpack zips both into a one-design behavior pack: open it on any Bedrock device to import, enable it on a world with cheats on, then use /function or /structure load as above. It holds both files, so it is refused (E014) above 10,000 commands; use --to mcstructure for those.

Common codes: E002 ragged grid, E003 character not in legend, E004 unknown type (hint has the closest names), E005 bad key or value (hint lists the allowed ones), E006 a component with nothing that holds it, E008 position outside the grid or on air, E011 port or test reference, E012 slug, E013 limit. Stage 2 warnings point at likely wiring mistakes: W101 dust too long, W102 a repeater or comparator pointing at nothing, W103 dust that connects nothing, W104 a torch that powers its own support (a burnout loop), W105 a piston that only Java's quasi-connectivity would power. W108 means a block's in-game mapping is not verified yet; it is safe to ignore while authoring. --strict makes any warning fail. The full list is in docs/diagnostics.md, and the --json output follows packages/core/schema/diagnostics.json.

Common mistakes

Worked example: repeater line

A lever drives 15 dust; a repeater refreshes the signal for a lamp further on.

{
  "format": "rsd/1",
  "meta": { "name": "Repeater line", "edition": "bedrock" },
  "legend": {
    "#": "stone",
    "L": "lever mount=floor",
    "-": "dust",
    ">": "repeater facing=east delay=2",
    "*": "lamp"
  },
  "layers": [["###################"], ["L--------------->-*"]],
  "ports": {
    "inputs": [{ "name": "A", "pos": [0, 1, 0] }],
    "outputs": [{ "name": "out", "pos": [18, 1, 0] }]
  },
  "tests": [
    { "set": { "A": 1 }, "afterTicks": 6, "expect": { "out": 1 } },
    { "set": { "A": 0 }, "afterTicks": 6, "expect": { "out": 0 } }
  ]
}

Layer 0 is a stone floor; layer 1 holds the circuit. The lever is [0,1,0], the repeater [16,1,0] facing east (the direction the signal leaves), and the lamp [18,1,0].

Worked example: NOT gate

The lever's dust points into a stone block; the torch on that block's east face goes out when the block is powered, and the lamp beside the torch follows it.

{
  "format": "rsd/1",
  "meta": { "name": "NOT gate" },
  "legend": { "#": "stone", "L": "lever mount=floor", "-": "dust", "t": "torch mount=west", "*": "lamp" },
  "layers": [["#####"], ["L-#t*"]],
  "ports": {
    "inputs": [{ "name": "A", "pos": [0, 1, 0] }],
    "outputs": [{ "name": "out", "pos": [4, 1, 0] }]
  },
  "tests": [
    { "set": { "A": 0 }, "afterTicks": 4, "expect": { "out": 1 } },
    { "set": { "A": 1 }, "afterTicks": 4, "expect": { "out": 0 } }
  ]
}

mount=west names where the torch's support is: the stone at [2,1,0], west of the torch at [3,1,0].

Share link by hand

To show a design without any tools: write it as one line of minified JSON, then replace every % with %25, every space with %20 and every # with %23 (nothing else needs changing), and put the result after https://timrross.github.io/dustwright/view/#json= (a local build serves the same /view/). The site accepts at most 64 KB of fragment as the browser sends it, and browsers encode ", < and > again (%22, %3C, %3E), so keep the JSON under about 20 KB. For the NOT gate it starts https://timrross.github.io/dustwright/view/#json={"format":"rsd/1","meta":{"name":"NOT%20gate"},"legend":{"%23":"stone",...

Patterns

Known-good designs, each built and tested in game, in patterns/ (MCP: list_patterns, get_pattern): not-gate, or-gate, and-gate, xor-gate, rs-latch, repeater-line, pulse-extender (button held about 2.6 s), pulse-limiter (one 4-tick pulse), observer-clock, repeater-clock (button starts it), t-flip-flop, piston-door-2x2, hopper-sorter (load its filter hopper as its notes say).

Stamp them instead of copying: "include": [{ "pattern": "not-gate", "at": [x,y,z], "rotate": 90 }] puts the pattern's lowest corner at at, turned clockwise seen from above (0, 90, 180, 270). Leave those cells as air in the layers; write your own ports and tests at the stamped positions. A legend may then be empty.