# Hearthwright

Hearthwright is a Python library for composing furnished Minecraft Java 1.21.1 properties. Clients describe connected volumes, occupied levels, room programs, roofs, terrain relationships and outdoor spaces. The shared library resolves and constructs them, protects circulation, furnishes rooms, validates the resulting blocks and exports one deterministic Litematic region.

There is no building-family argument, reference dispatch or embedded collection of complete buildings. The examples contain architectural declarations using the same public classes. The five new compositions use those classes without custom geometry or validator code.

## Run the delivered artifacts

From `/work/generator/workspace`:

```sh
/opt/venv/bin/python generate.py
/opt/venv/bin/python generate.py --seed 73 --output samples/my-manor.litematic
/opt/venv/bin/python -m examples.stone_cottage --seed 8 --output samples/cottage.litematic
/opt/venv/bin/python -m examples.hillside_studio --seed 31 --output samples/studio.litematic

# All thirteen independent clients, seeds 0 and 1, with reload validation and manifest.
/opt/venv/bin/python -m scripts.sample_batch

# All clients: two exterior views and each occupied floor elevation as a cutaway.
/opt/venv/bin/python -m scripts.render_batch
# Include every saved seed in the render review:
/opt/venv/bin/python -m scripts.render_batch --all-seeds

/opt/venv/bin/python -m pytest -q
/opt/venv/bin/python -m scripts.audit --count 64
```

`generate.py` is a standalone architectural client. Its default seed is **20260925**, and its default destination is **output.litematic**. Every example accepts the same `--seed` and `--output` options. Example modules run with `python -m examples.NAME`; the name selects a Python client file, not a library building preset.

The environment supplies MCIO, NumPy and the Java state registry. No downloads or reference-script execution occur. To use the package from another directory:

```sh
export PYTHONPATH=/work/generator/workspace
cd /tmp
/opt/venv/bin/python /work/generator/workspace/generate.py --seed 73 --output /tmp/property.litematic
```

A standard `pyproject.toml` is also supplied for environments with setuptools >= 68. The supplied container does not include that build dependency; use the tested `PYTHONPATH` route here. MCIO and the block registry remain external runtime dependencies. On another machine, set `MCIO_ROOT` to the MCIO source directory and `MINECRAFT_STATES` to a copy of the authoritative `1.21.1-blocks.json`. Rendering additionally uses the supplied MCRender installation and Xvfb. Generation and validation do not require the renderer or a GPU.

## A short independent client

```python
from pathlib import Path
from hearthwright import (
    Attachment, Choice, Design, Join, Level, Materials,
    Roof, Site, Span, Volume, generate,
)

spec = Design(
    "Birchwater workshop",
    volumes=(
        Volume("home", width=Span(13, 17, 2), depth=15,
               levels=(Level(("living", "kitchen")),
                       Level(("sleeping", "study"), attic=True)),
               roof=Roof(ridge=Choice("x", "z"), pitch=1.0)),
        Volume("work", width=9, depth=11,
               join=Join("home", side="east", align=Choice("front", "back")),
               levels=(Level(("workshop", "storage")),),
               roof=Roof(form="hip", pitch=.5)),
    ),
    materials=Materials(wall="plaster", frame="spruce", roof="sandstone"),
    site=Site(planting="temperate"),
    attachments=(Attachment("porch", "home", side="south", width=7,
                            depth=3, cover="pergola"),),
)

building = generate(spec, seed=37)
building.validate().require_valid()
building.export(Path("workshop.litematic"))
building.verify(Path("workshop.litematic"))
```

Ordinary clients need no absolute coordinates, placement loops, blockstates, geometry repairs or NBT code. The library also works without the CLI:

```python
from hearthwright import Building, resolve, validate

plan = resolve(spec, seed=37)       # Inspect choices before allocating blocks.
print(plan.summary())
print(plan.signature())
report = validate(Path("workshop.litematic"))
restored = Building.load(Path("workshop.litematic"))
assert restored.validate().valid
```

## Architectural API and domains

All declaration classes are frozen dataclasses. Parameters marked **sampled** accept a concrete value, `Choice(a, b, ...)`, or, for integers, `Span(low, high, step=1)`. Spans contain at most 256 declared values to bound sampling resources. A span is inclusive at its upper bound; values advance from `low` by `step`. Each choice starts with a seeded uniform draw over locally feasible alternatives. Bounded backtracking can change that selection; the library does not promise uniform sampling of complete designs. Concrete values remain requirements.

| Declaration | Parameters and dependencies |
| --- | --- |
| `Design(name, volumes, ...)` | One dwelling or property. Supply ordered volumes, material roles, site, optional entrance, attachments, features and resource limits. Names identify components and joins; they never select implementations. |
| `Volume(name, width, depth, levels, ...)` | Sampled outer width 9..25 and depth 9..23, tightened by the actual insets and circulation. Every additional mass needs a `Join` to an earlier mass. |
| `Volume.levels` | A tuple of 1..4 `Level` declarations, or `Choice` of such tuples. This permits real seeded floor-count and program changes. Root envelopes allow at most two levels. |
| `Level(rooms, layout, height, inset, wall, attic)` | `rooms` is a tuple of functions. Sampled `layout`: `open`, `cross` (split across width), or `long` (split across depth). Split layouts need at least two functions and at least 15 blocks on the splitting axis. Programs divide between two actual rooms with a connecting door. Regular floor spacing is 5..7. |
| Level dimensions | `inset` 0..3 shrinks that level relative to its volume. Every resulting floor is at least 9 by 9 before the wall thickness. An upper floor wider than the lower receives posts, beams and knee braces automatically. An attic must be last and has a three-block knee-wall zone beneath its sloping roof. Its declared height is not an extra occupied floor. |
| Room functions | `living`, `kitchen`, `sleeping`, `storage`, `workshop`, `study`, `enchanting`, `brewing`, `trading`, `fishing`. Furniture size responds to available room area. Multiple functions can share an open room. Excessive programs that cannot fit with clear access receive a diagnostic rather than losing functions. |
| `Join(to, side, align, level)` | Sampled cardinal `side`; sampled `front`, `center`, or `back` alignment along the shared edge. `level` selects the target storey, and the wing inherits its floor elevation. `front` means the low coordinate end of that edge, independent of the entrance direction. Joined masses share a real doorway, not a declared graph edge alone. |
| Vertical circulation | Sampled `stairs` or `ladder`. Straight stairs need common depth at least lower-storey height + 6 and common width at least 11, plus both landings and clearance from openings. Successive stair flights alternate sides. A ladder needs a shared backed shaft and a clear exit. Concrete stairs never silently become ladders. |
| `Roof(form, ridge, pitch, eaves, dormers, cupola)` | Sampled `gable`/`hip`; sampled `x`/`z`/`long`/`short` ridge; sampled pitch .5, 1.0 or 1.5; eaves 1 or 2. Long/short resolve from the mass proportions. All roofs have solid backing and filled risers. |
| Attics and dormers | Attics require pitch >= 1. Dormers are sampled 0..2, require a gable with pitch >= 1, and two require ridge length >= 17. Their integrated height field closes valleys and side transitions. Cupolas are sampled booleans and form sealed, solid-cored ridge lanterns, not inaccessible occupied rooms. Use a joined multi-level volume for a usable tower. |
| Root envelope | `envelope="roots", roof=None`, sampled `root_spread` 3..6 and `root_crown` 3..7. A mound, tapering trunk, swept oriented roots and hooked crown surround carved, chamfered rooms. The dark cap seals into the trunk. Openings receive daylight tunnels through the hill. Root levels use open programs, not rectangular partitions. |
| Foundations and water | `raised` 0..7 raises the first volume's base above the normal floor datum. `supports="foundation"` fills a continuous foundation; `"posts"` drives piles to the ground or bed. Joined volumes inherit elevation and must omit `raised`. Water sites require posts and an initial raised value >= water depth + 1. |
| `Entrance(volume, side, level, bay)` | Cardinal side and an existing occupied level. Sampled `left`, `center`, or `right` bay. The exterior approach uses the real entry and terrain heights, rises or descends as required, and connects through any matching terrace/deck. Root approaches continue beyond the full mound and buttresses. Default: first volume, south side, ground floor, center bay. |
| `Attachment(kind, to, ...)` | `porch`, `deck`, `terrace`, `balcony`, `market`; cardinal sampled side, occupied level, sampled depth 3..7, sampled width `full` or 5..facade span. Cover is `open`, `pergola` or `canopy`. Every attachment receives a door, supports, circulation, furniture and light. Nearby attachment lamps can also serve the entrance. Pergolas have spaced rafters with open sky between them. |
| `Feature(kind, side, scale)` | `pool`, `field`, `well`, `windmill`, `landing`, `garden`. Sampled side and scale 5..11. Windmills require a plot at least 9 wide, including their sail clearance. Plots are allocated outside the complete massing and attached floors. Walks avoid buildings, approach stairs and other feature plots. Landings on dry sites excavate a contained water reach. |
| `Site(terrain, margin, planting, rise, water_depth, paving)` | Terrain `meadow`, `slope`, `water`, `hill`; margin 3..12; rise 2..7; shallow water depth 1..3. Planting `temperate`, `mediterranean`, `conifer`, `wetland`, `sparse`. Paving `garden`, `courtyard`, `yard`; water uses garden plus supported attachments. A slope rises toward the north and is excavated out of all inhabited cells. Hill sites require a root envelope, whose rooms and root parameters define the mound; use a slope for framed earth-sheltered buildings. The rise parameter belongs to slopes. |
| `Materials` | Wall roles: `stone`, `masonry`, `plaster`, `white`, `blue`, `timber`, `earth`. Roof roles: `pale_stone`, `sandstone`, `slate`, `gray`, `red`, `tile`, `dark_wood`. Frame/floor use vanilla wood roles; trim uses wood or sandstone. Accent is one of the 16 vanilla colors. Shutters can be disabled. Role palettes resolve to validated blocks, including distinct full backing, stair and slab materials. |
| `Limits` | Default horizontal extent 128, vertical extent 96, volume 1,500,000 cells, and 300,000 non-air blocks. Configure these explicitly for larger coherent properties. The library derives actual bounds, including roots, sails, trees and terrain, and rejects excesses; it never clips blocks. |

The references' scale is intentional. A width parameter is bounded; it is not a route to arbitrarily large halls with the same tiny furnishings. Large rooms receive dining groups, wardrobes, longer work counters, shelving and additional lamps. Rooms above 150 usable floor cells also receive function-specific reading tables, dressing furniture, pantry/island groups or parts racks; compact rooms use smaller functional groups.

Dependencies are resolved before geometry where possible. The planner backtracks within declared finite domains when an earlier choice causes a later conflict. It examines at most 128 candidates by default; `resolve(..., max_candidates=N)` exposes that planning budget. It returns an actionable error for contradictions or budget exhaustion, without switching to a fixed fallback building. A user can reduce conflicting choice domains or relocate a wing. Construction errors such as an overfull room program remain errors; generation does not suppress failed validators or repair a particular seed by hand.

## Reproduction and records

Seeds are signed 64-bit integers, including zero and negative values. All sampling, furnishing choices, terrain texture and plants use seeded `random.Random` instances. Geometry ordering is deterministic. There are no time-based seeds, global random calls, network calls or seed-specific exceptions. The same declaration and seed produce the same geometry and bytes in the supplied environment. Python's seed stream can map distinct seeds to the same decisions; that is not evidence of additional structural variety.

Export writes:

- One `.litematic` region, Java data version 3955, with zeroed creation/modification timestamps.
- An adjacent `.json` sidecar: the original specification, seed, resolved masses and room layouts, roof choices, component coordinates, protected routes, expected openings, support paths, furniture, light locations, inventories, actual bounds and structural signatures.

The sidecar uses world coordinates, which can be negative. `origin` maps them into positive schematic coordinates. `Building.load(path, metadata=...)` accepts the adjacent sidecar by default, a metadata dictionary, or an explicit sidecar path. Keep the sidecar with the export for architecture-aware validation. The Litematic remains independently usable in Minecraft.

`verify(path)` compares the entire non-air coordinate set and every final blockstate after reload. It uses MCIO's parsed block names and explicit property mappings. Property order is ignored; changed names, values or omitted properties still fail. Multiple writes to a cell are valid: the final state is what is checked. Actual container NBT is reloaded and checked against the documented supplies, rather than trusting the JSON alone.

## Validation and diagnostics

`generate(...)` validates by default; `Building.export(...)` validates again before writing. `generate(..., validate=False)` is intended for rule development and diagnostics, not for publishing an unchecked sample. `validate` returns a `Report` and does not throw on ordinary geometry failures. `report.require_valid()` raises `ValidationError`, retaining the full report.

```python
report = building.validate()
for issue in report.diagnostics:
    print(issue.rule, issue.component, issue.location, issue.message)
```

Representative rules:

| Rule | Actual evidence checked |
| --- | --- |
| `block.state`, `bounds.*` | Every used name/property/value against the supplied 1.21.1 registry; final geometry and recorded export bounds/resource limits. |
| `multiblock.door`, `multiblock.bed` | Matching halves or adjacent heads/feet, directions, explicit properties and support. |
| `support.column`, `.stair`, `.ladder`, `.light` | Unbroken recorded load paths; supported treads; directionally backed ladder rungs; attached lanterns. Plant and crop substrates are also inspected. |
| `clearance.route`, `support.route` | Actual foot/head blocks for every protected passage and actual floor support. |
| `reachability.*` | A walkable transition from surrounding terrain to the outer landing, followed by voxel traversal through the final geometry to every room anchor, every usable cell at its floor, outdoor working target and furniture interaction point. Rising movement requires a suitably directed stair or ladder; ordinary one-block jumps cannot mask a broken staircase. |
| `envelope.leak`, `.intrusion` | Flooding of actual interior voids against the envelope, with closed doors as weather seals. Full roof backing is required around partial stair/slab models. Water and hill material inside rooms are rejected. |
| `opening.glazing`, `.clearance` | The expected glazing is still glass and room-side reveals are clear. |
| `program.missing`, `furnishing.changed`, `lighting.room`, `.approach`, `.feature` | Every assigned function has realized furniture, final furniture states remain present, and rooms, entrances, approaches and working plots retain actual nearby lights. |
| `inventory.changed` | Reloaded Java block-entity contents match the supplies recorded for each container. |

Reservations protect the paths derived from empty, already constructed geometry before furniture placement. Furniture candidates must have actual support and a reachable interaction cell and must preserve the connected usable floor area; their access routes become reservations before the group is placed. Final validation reruns traversal after furnishing, planting, fence connection updates and all other passes. A declared room connection is never accepted as proof of reachability.

The navigation model is conservative and tailored to the emitted vocabulary. Closed wooden doors are treated as operable; locked doors, jumping puzzles, swimming routes and redstone doors are outside the contract. This is not a Minecraft engine or a proof of every possible client specification. Weather validation treats full cubes and closed doors as seals and requires solid backing behind partial roofs. Light placement is validated; exact Minecraft light propagation, mob spawning and renderer brightness are not simulated.

## Implementation and extension points

The modules have separate responsibilities:

- `api.py`: immutable declarations and bounded domains.
- `plan.py`: relative spatial resolution, constrained choice search, floor masks and plot allocation.
- `build.py`: shared construction pipeline, openings, circulation and artifact API.
- `roofs.py`: backed gable/hip height fields, roof unions, dormers, ridge lanterns and root sweeps.
- `site.py`: terrain, supports, outdoor floors, property features, walks and planting.
- `furnish.py`: function-to-furniture rules and placement around reservations.
- `navigation.py`: geometry-based movement and path recovery.
- `blocks.py`: authoritative blockstate construction, sparse geometry, NBT and deterministic transport.
- `validate.py`: independent diagnostic rules over in-memory or reloaded geometry.
- `components.py`: reusable component extension contracts.

`@validation_rule` registers an additional pure rule with signature `(grid, metadata, report)`. A rule calls `report.add(rule_id, component, message, location)` for failures. Existing generation/export validation invokes registered rules automatically.

`register_site_component(SiteComponent(...))` adds a reusable site capability without modifying planning or dispatch. A component supplies its name, version, inclusive scale domain, `realize(context, resolved_feature)` and optional validation rule. The planner allocates its plot alongside built-in features and records its version. The renderer callback uses `context.g`, `context.ground` and the resolved `Rect`; it records its support columns and openings, reserves clear cells through `grid.reserve`/`grid.route`, records a light owned by the feature, sets `feature['target']`, and appends the matching metadata feature. Shared path planning then connects that target, and shared validation checks its realized access. The extension test demonstrates this contract with a tool cache. Reinstall/register the same component version when reproducing an extended design.

New roof or envelope algorithms can follow the existing component pipeline: resolve a bounded footprint and interfaces, contribute interior/clearance masks, realize geometry before furniture, and add diagnostics over final cells. The current public roof vocabulary remains deliberately limited to the implemented, tested forms. Adding an unsupported string never silently approximates it.

## Reference-derived capabilities and new compositions

All eight supplied images and generators were inspected. The source snapshots remain unchanged, with their provenance in `references/README.md`. Useful ideas were extracted into shared mechanisms rather than importing or executing those generators.

| Reference | Capabilities exercised by the independent client |
| --- | --- |
| Mediterranean villa | Offset unequal-height wings, pale plaster and turquoise shutters, shallow tile roofs, two terrace levels, open pergolas, planted courtyard and pool. |
| Merchant manor | Masonry undercroft, elevated public entrance, supported terrace, exterior stair, timber/plaster upper floors, dark roof, study tower and striped market canopy. |
| Red-roof farmstead | Steep red roof with pale verges, inhabited attic, dormer, ridge lantern, working field, sheltered well and a separate windmill plot. |
| Root house | Earth and stone mound, irregular hollow floor masks, broad swept dark roots, forked crown, sealed trunk cap, recessed entrance and stacked furnished rooms. |
| Sandstone cottage | Broad pale roof with dark trim, connected projecting bay, excavated slope floor, elevated entrance and stairs, and a small waterside jetty. |
| Stilt water cabin | Low overhanging timber roof, piles in shallow water, railed deck, supported descending approach, landing and stocked fishing/cooking quarters. |
| Stilted manor | Recessed masonry ground floor, wider supported upper floor, long beams and braces, steep inhabited roof, dormers, ridge lantern and balcony. |
| Stone cottage | Compact stone/timber footprint, restrained pale gable, recessed entry, clear glazing, planters, domestic main room and backed ladder loft. |

The novel clients are a **canal apothecary** (piled plaster home, hipped roof, brewing wing and pergola), **woodland archive** (small dwelling plus a taller library wing), **orchard house** (sampled floor count, domestic loft, covered working terrace and field), **hillside studio** (a hollow root dwelling joined to a conventional workshop), and **courtyard house** (three connected masses around a sheltered entrance court). Their geometry is entirely generated by the same library.

This is an architectural interpretation of the distribution, not an exact replica of any screenshot. Logos, text, presentation borders and shader effects are excluded. Boats are supplied as inventory items at moorings; the library does not place vehicle entities. Ridge lanterns and the small windmill are architectural features, not redstone machines. Container stock is modest. Exterior chimneys, curved roofs, arbitrary polygons and arbitrary-height stair flights are not current public capabilities.

## Evidence and review artifacts

The delivered results and observed counts are recorded in [VALIDATION.md](VALIDATION.md).

`samples/manifest.json` records the finite delivered sample batch, client paths, seeds, complete resolved decisions, semantic reload counts and validation reports. Two seeds per client change real dimensions, alignments, circulation, partitions, ridge directions, pitch, root morphology or floor counts. The manifest's observed counts describe this batch only.

`test/test_corpus.py` validates thirteen clients at twelve fixed seeds, including both signed seed boundaries, plus targeted size/pitch and interaction cases: four storeys, hipped attics, maximum water depth, wide eaves, and entrances facing all four directions on flat and sloping sites. Other tests check determinism, semantic property equality, supported custom components, contradictory specifications, and intentional damage to stairs, doors, beds, posts, ladders, glazing, roofs, lighting, furniture, inventories and bounds. Temporary artifacts live under `test/.tmp` and are removed. `scripts.audit` supplies a separately rerunnable consecutive-seed audit and records failures without hiding them.

`previews/index.html` is an offline labeled review sheet. Each representative property has two exterior angles plus cutaways at every occupied floor elevation, including taller joined wings. Cutaways are geometry-only review images; their open walls/roofs are not defects in the exported complete building. `render_batch.mjs` reuses the supplied renderer configuration; `render.mjs` remains a convenient four-view renderer for a single file.

Tests and the finite seed audit provide sampled and targeted evidence. They do not exhaust the admitted parameter domains or establish a mathematical guarantee for every new composition. Invalid explicit relationships, excessive programs and exhausted planning budgets produce diagnostics so the client can narrow its requirements. The library does not claim unsupported combinations as available architecture.
