# Hearth extension components

This package is a self-authored extension exercise against Hearth's public component protocol.
It is separate from the kernel and the original building vocabulary. No extension accesses private
scene state, edits the shared block store, assigns owners, or reconstructs ancestry. Generation uses
Python's standard library; transport and rendering remain optional host operations.

## Connect existing components

```python
from hearth import Scene, Box, Frame
from hearth.environment import Terrain, gradient, radial
from hearth_extensions import Polygon, Pavilion, Endpoint, Link

domain = Box((-12, -4, -12), (60, 65, 65))
environment = Scene(7, domain=domain)
field = gradient(.012, -.008, 3) + radial(19, 22, 14, -2)
environment.place("land", Terrain(domain, field, water_level=3))
environment.finalize()                  # Freeze terrain before deciding contact geometry.

scene = environment.fork(seed=23)
shape = Polygon.chamfered(13, 13, 2)
scene.place("reading", Pavilion(shape, "/land", "library"))
scene.place("workshop", Pavilion(shape, "/land", "workshop"), frame=Frame((25, 0, 29)))
a, b = Endpoint("/reading", "south"), Endpoint("/workshop", "north")
scene.place("gallery", Link(a, b, "/land", covered=True),
            bindings=(a.binding(), b.binding()))
scene.finalize()
```

`Endpoint` consumes an advertised `access` or `public-access` port, its world frame, region and
capacity. Local -Z is the outward normal. `Link` first reads both interfaces, measures terrain and
actual bed support along its ribbon, pins the endpoint elevations, and solves a bounded height
profile. It proposes trestles, deck, guards and an optional canopy as real children. Only their
terrain bindings obtain scoped fill authority. Endpoint landing cells are reused without transferring
ownership. `connected_to` records bindings, while `connects` records the two structural associations.

For a containing component, `connection(key, a, b, terrain, **options)` returns the fully bound
`Child`. No caller needs the deck coordinates, support locations or the host's internal approach path.
Discover installed interfaces with `scene.view.offers(host, kind)` and inspect feasible domains with
`component.capability()` or `scene.view.capability(path)`.

## Curves, walls and attachments

```python
from hearth_extensions import Polyline, Boundary, IlluminateBoundary
from hearth_extensions.boundaries import BoundaryChain

curve = Polyline.bezier(((0, 0), (7, 25), (28, -3), (37, 20)))
# In an independently prepared, empty strip of terrain:
scene.place("boundary", BoundaryChain(curve, height=2, spacing=7))
scene.place("lamps", IlluminateBoundary("/boundary", every=2))
```

The strip must be free of incompatible existing structures. A boundary does not excavate or displace
other owners. It follows measured ground and checks each substrate column and neighboring grade.
`BoundaryChain` partitions long contours into bounded components and forwards their `mount` ports.
The cross-segment join retains the same grade limit. `IlluminateBoundary` discovers those ports;
its lamps remain semantically attached to the real boundary despite being created by another parent.
A mount carries a local tangent frame, small-fixture load fact, capacity and a zero-replacement grant.
An installer cannot treat that as permission to carve the wall.

`Polyline` stores explicit finite 2D control points. Cubic Bezier construction samples a curve into
that same representation. Arc projection, deterministic cardinal rasterization and distance ribbons
are shared geometric operations. Endpoint caps affect only their local ends; a bending route may
return to the same Z as its start. Integer polygons supply exact raster footprints, perimeters and
concentric roof courses. These representations were chosen for terrain sampling, joins and usable
Minecraft routes, rather than introducing a full mesh dependency.

## Graphs and architecture

```python
from hearth_extensions.graphs import Campus, Vertex, Edge

campus = Campus(
    (Vertex("a", Pavilion(shape, "/land", "library"), Frame()),
     Vertex("b", Pavilion(shape, "/land", "workshop"), Frame((25, 0, 29)))),
    (Edge("a", "south", "b", "north", covered=True),),
    terrain="/land", envelope=domain, landscape=False,
)
```

Place this component on a fresh prepared scene. `Campus` checks the explicit graph, places its
vertices through their shared protocol, discovers access ports and composes physical connections.
It forwards public access and remains a component. A `Platform`, `Pavilion`, or original `Building`
can occupy a compatible vertex without changing the graph parent. There is no component-type
dispatch in that parent. Vertex frames and edge intent are locked; the connection planner adapts
contact geometry rather than silently moving buildings.

`Pavilion` adds non-grid polygon footprints, multiple cardinal entrances, hipped roofs and a roof
lantern. `PolygonShell` exposes installation regions; `Facade` uses existing Hearth windows and
doors through those interfaces. Querying glass gives window -> shell -> pavilion -> campus.
The foundation and each approach use the original measured site adapters.

`HallInterior` places `WorkBay` children around a protected three-cell circulation cross. It fits
functional library, workshop, kitchen, bedroom, living or storage stations with explicit interaction
ports, inventories and support. Actual routes reach every station. Its negotiated decisions report
requested, placed and omitted optional bays; the minimum is mandatory. Wider halls acquire more
stations. It uses the same floor/space observations and validators as other components.

Two short programs in `programs.py` describe a branching campus and a three-vertex perimeter circuit.
Their seeds change dimensions, height, roof intent, wing/storey composition, edge rise and vertex
count where declared. A frozen field expression creates the basin and slopes before either program
runs. The original default street settlement remains available through the unchanged CLI.

## Supported domains and limits

| Capability | Current contract |
|---|---|
| Link | Simple non-self-crossing route, length 7..120 by default, body width 3 or 5, rise 0..3 |
| Endpoints | Matching world positions, compatible outward tangents, offered mouth at least 3 cells wide, free capacity; no host write authority |
| Contact | At least two real solid bed cells, posts at most 8, taxicab supported span at most 6, fill at most 800; existing terrain grant can be stricter |
| Boundary | Simple open contour, height 1..5, mount spacing 4..12, adjacent base change 0 or 1; at most 220 raster cells per segment |
| Boundary chain | Segment chunk 4..220 (default 96); whole composition also subject to kernel dimension, instance and block limits |
| Pavilion | Normalized simple polygon, width/depth 13..25, cardinal entry midpoints on its boundary, wall height 5..7, six purposes |
| Hip roof | Concentric polygon courses with run 1, 2 or 3; optional bounded roof lantern; no general roof intersection solver |
| Hall interior | Default 2..8 fitted functional bays; explicit minimum/maximum and area-per-bay controls; protected central aisles |
| Platform | Width/depth 5..27, freeboard 1..3, measured foundation, oriented cardinal ports |

Footprint holes, arbitrary concave hall layouts, curved door planes, self-crossing routes and full
continuous collision physics are outside the tested domain. The connector's own start/end offers are
point contacts; they are not advertised as broad platforms for chaining another wide ribbon. Use a
public landing/platform vertex for a junction. Graph placement is explicit intent, not an autonomous
packing or city-layout solver. Covered links are open galleries with canopies, not sealed indoor rooms.
The finite contact strategies still reject terrain beyond their bounds.

No client needs private geometry in the shipped examples. Callers do need the public coordinate
convention, compatible port kinds, outward frames, width facts and terrain edit interface. Geometry
implementations still contain architectural algorithms; adding a new vocabulary is ordinary component
work, not something arbitrary descriptions synthesize automatically.

## Replay and validation

Import `hearth_extensions` before reloading an extension artifact so the registered guard/span
validators are available. No serialized Python is executed. Missing validator handlers fail explicitly.

```sh
python -m examples.extension_generate campus --seed 3 --environment-seed 7 --output "campus.litematic"
python -m examples.extension_generate perimeter --seed 3 --environment-seed 3 --output "perimeter.litematic"
python -m examples.extension_inspection --seed 0 --input samples/extensions/campus-0.litematic --output samples/extensions/inspection.litematic
python -m examples.extension_replay --seed 3 --environment-seed 7 --output reports/extension-corpus/replay.json
python -m examples.extension_assess --seed 0 --output reports/extension-reassessment --artifacts --samples samples/extension-reassessment
python -m examples.extension_audit --seed 0 --manifest samples/extension-reassessment/replay-manifest.json --output reports/extension-reassessment/artifact-audit.json
python -m pytest -q test
```

Keep prior reports and their matching artifact pairs when changing output roots. The seed-zero
assessment policy and final results live in `reports/extension-corpus/`; they supplement, rather than
replace, first-phase evidence. Native import tests forbid new native/transport dependencies during
generation. The user's prior independent Pyodide report is not a browser rerun of this revision.
