# 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](#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.

```json
{
  "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

- +X is east, +Y is up, +Z is south. `[0,0,0]` is the north-west bottom corner.
- `layers[y]` is one horizontal slice, listed **bottom to top**.
- `layers[y][z]` is one row string, listed **north to south**.
- The character at index `x` of a row is the block at `x`, **west to east**.
- So the block at `[x, y, z]` is `layers[y][z][x]`.
- `.` and space are air.
- Every row in the design has the same length, and every layer has the same number of rows. Pad with `.`.
- Legend keys are single printable ASCII characters other than `.` and space.

## 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.

- `door` occupies two cells: put it in the lower cell and leave the cell above as air (`.`); the upper half is
  placed for you.
- `wool` and `concrete` take `color=` (white, orange, magenta, light_blue, yellow, lime, pink, gray, light_gray,
  cyan, purple, blue, brown, green, red, black). Use colours to group sub-circuits.
- `powered`, `lit`, `on`, `pressed` and `power` describe the **initial** state; the game recomputes them.

## Optional sections

- `ports`: `{ "inputs": [{ "name", "pos": [x,y,z] }], "outputs": [...] }`. Names are 1 to 32 of `A-Z a-z 0-9 _ -`,
  unique across inputs and outputs. Inputs sit on a lever, button or pressure_plate; outputs on a lamp, piston,
  sticky_piston, door, trapdoor, note_block, dust or comparator.
- `tests`: truth-table cases, `{ "set": { input: 0|1 }, "afterTicks": n, "expect": { output: value } }`.
- `blocks`: overrides applied after layers, `[{ "pos": [x,y,z], "block": "<spec>" }]`, one per position.
- `annotations`: `[{ "pos": [x,y,z], "text": "..." }]`, pinned to a block, shown in the viewer.
- `steps`: build steps, `{ "title", "layers": [y, ...] }` or `{ "title", "positions": [[x,y,z], ...] }`. Default:
  one step per layer.
- `meta`: `name` (1 to 80 printable characters), `slug` (optional; `a-z0-9` runs joined by `-`, at most 48, not a Windows device name such as `con` or `nul`;
  derived from the name when omitted), `description` (at most 2,000), `edition` (`bedrock`, the default,
  or `java`), `gameVersion` (e.g. `1.26.50`; Java `26.3`).

## Tests: how they run

- Ticks are **redstone ticks**: 1 redstone tick = 2 game ticks = 0.1 s.
- Each case starts from the design as written, runs until stable (at most 64 ticks), applies `set`, runs
  `afterTicks` more ticks (at most 1,000), then reads `expect`. Cases are independent.
- Levers and pressure plates hold the value (`0` off, `1` on). A button value of `1` is one press (stone 10 ticks,
  wood 15).
- Lamps, pistons, doors, trapdoors and note blocks report `0` or `1`. Dust and comparators report their power
  level, `0` to `15`.
- Write `ports` and `tests` **before** placing blocks, from the brief.
- `rsd validate` runs the tests in an approximate simulator (stage 3). T001: a case failed; the message lists
  expected and actual per output. T002: `afterTicks` above 1,000, or the design is too big to simulate that long.
  T003 (info): the case was skipped because it needs something not modelled (a comparator reading a container, a
  daylight sensor, a piston moving a container or a slime or honey block); check it in game.
- Bedrock delays the simulator uses (Java: below): torch, comparator, lamp, door and piston 1 tick; repeater 1 tick per delay step;
  observer reacts after 1.5 ticks and pulses for 1 (inferred from the observer clock period measured in game). Leave a tick or two of margin in
  `afterTicks`. If a case passes in game but fails here, the game is right: report it.
- The viewer's Power menu shows any test case tick by tick: dust brightness is its level.

## Bedrock rules that bite

- **No quasi-connectivity.** A piston is powered only by power at the piston itself: an adjacent powered block
  (above it too, unless it faces up), dust pointing into it, a torch beside it, a repeater facing into
  it. Power two blocks above it, or diagonally above, does nothing (that is Java's quasi-connectivity). Each
  piston in a stack needs its own power.
- Dust loses one level per block; after 15 blocks it is dead. Insert a repeater.
- A torch turns off when the block it hangs on is powered, and strongly powers the block above it.
- A redstone block powers components and dust next to it, not solid blocks.
- Patterns from Java tutorials often fail on Bedrock. If unsure, keep the circuit simple and test it.

## Java Edition

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

- **Quasi-connectivity works**: a piston also fires from power at the block above it, but only when it gets a
  block update. Prefer direct power; the simulator fires it at once, the game may not.
- Measured delays, in game ticks (one `afterTicks` tick is 2): torch, comparator 2; repeater 2 per step; observer 2, pulse 2;
  piston 3; lamps light at once and go dark after 4; doors at once.
- Supports: levers need a solid face, like buttons; honey holds nothing; a hopper's top holds dust, repeaters and
  plates, not torches, levers, buttons or doors. Slime conducts.
- Export with `rsd export <file> --to schem -o <dir>` (WorldEdit: `//schem load`, `//paste`) or
  `--to litematic` (Litematica). There is no `.mcfunction` or `.mcpack` for Java.

## 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

- Rows of different lengths, or a layer with a missing row (E002). Count characters.
- Reading layers top-down. `layers[0]` is the **bottom**.
- Mixing up `mount` and `facing`: torches, levers and buttons use `mount` (where the support is).
- A component with nothing under it: dust, repeaters, comparators, pressure plates and floor torches need a
  block below that holds them; a bottom slab does not (E006).
- A port on air, or an output port on stone (E008, E011).
- Relying on quasi-connectivity for a piston (see above).

## Worked example: repeater line

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

```json
{
  "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.

```json
{
  "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.
