{"timestamp": "2026-09-26T22:22:50.799Z", "command": "cat hearth/kernel/scene.py", "exit_code": 0, "seconds": 0, "output_tail": "\n        targets = [path] + list(dependents)\n        recipes = {}\n        for p in targets:\n            if p in trial._recipes:\n                recipes[p] = trial._recipes[p]\n            elif p == path and component is not None:\n                n = trial.nodes[p]\n                recipes[p] = (component, n.parameters.get('arguments', {}), n.frame, n.bindings, n.parent, n.scope, n.complete)\n            else:\n                raise ContractError('replay-implementation', p, conditions='Reloaded branch needs an explicitly supplied component implementation')\n        for p in targets:\n            trial._remove_branch(p)\n        for p in targets:\n            comp, oldparams, frame, bindings, parent, scope, complete = recipes[p]\n            if p == path and seed is not None:\n                scope = scope.child('override', seed)\n            trial._instantiate(p, component if p == path and component is not None else comp, params if p == path and params is not None else oldparams, frame, bindings, parent, scope, complete)\n        trial._check(True)\n        self._commit(trial)\n        self.events.append({'kind': 'regenerated', 'targets': targets})\n\n\ndef describe_inputs(value):\n    \"\"\"Record declared public inputs without concrete-component dispatch.\"\"\"\n    if value is None or isinstance(value, (str, int, float, bool)):\n        return value\n    if isinstance(value, (list, tuple)):\n        return [describe_inputs(v) for v in value]\n    if isinstance(value, dict):\n        return {str(k): describe_inputs(v) for k, v in value.items()}\n    if is_dataclass(value):\n        return {'type': type(value).__module__ + '.' + type(value).__qualname__, 'inputs': {f.name: describe_inputs(getattr(value, f.name)) for f in fields(value)}}\n    if callable(value):\n        return {'program': value.__module__ + '.' + value.__qualname__}\n    return {'type': type(value).__module__ + '.' + type(value).__qualname__, 'inputs': {k: describe_inputs(v) for k, v in vars(value).items() if not k.startswith('_')}}\n"}
{"timestamp": "2026-09-26T22:22:50.807Z", "command": "cat hearth/kernel/validation.py\ncat hearth/composition.py\ncat hearth/space.py", "exit_code": 0, "seconds": 0, "output_tail": "r turns')\n        object.__setattr__(self, 'turn', self.turn % 4)\n\n    def vector(self, p: Pos) -> Pos:\n        x, y, z = p\n        for _ in range(self.turn % 4):\n            x, z = -z, x\n        return x, y, z\n\n    def point(self, p: Pos) -> Pos:\n        return tuple(a + b for a, b in zip(self.origin, self.vector(p)))\n\n    def inverse(self, p: Pos) -> Pos:\n        return Frame(turn=-self.turn).vector(tuple(a - b for a, b in zip(p, self.origin)))\n\n    def box(self, box: Box) -> Box:\n        return Box.enclosing(self.point(p) for p in box.corners())\n\n    def compose(self, other):\n        return Frame(self.point(other.origin), (self.turn + other.turn) % 4)\n\n    def state(self, state: str) -> str:\n        from .blocks import parse, state_of\n        name, props = parse(state)\n        dirs = ['north', 'east', 'south', 'west']\n        rotated = {}\n        for key, value in props.items():\n            if key in dirs:\n                key = dirs[(dirs.index(key) + self.turn) % 4]\n            if key == 'facing' and value in dirs:\n                value = dirs[(dirs.index(value) + self.turn) % 4]\n            if key in ('shape', 'orientation') and any(v in dirs for v in value.split('_')):\n                from .blocks import SCHEMA\n                tokens = [dirs[(dirs.index(v) + self.turn) % 4] if v in dirs else v for v in value.split('_')]\n                value = '_'.join(tokens)\n                allowed = SCHEMA[name.removeprefix('minecraft:')][0][key]\n                if value not in allowed:\n                    matches = [candidate for candidate in allowed if sorted(candidate.split('_')) == sorted(tokens)]\n                    if matches:\n                        value = sorted(matches)[0]\n            if key == 'axis' and value in ('x', 'z') and self.turn % 2:\n                value = 'z' if value == 'x' else 'x'\n            if key == 'rotation':\n                value = str((int(value) + 4 * self.turn) % 16)\n            rotated[key] = value\n        return state_of(name, **rotated)\n"}
{"timestamp": "2026-09-26T22:22:50.814Z", "command": "pwd\ncat /work/AGENTS.md\ncat hearth/kernel/contracts.py\ncat hearth/kernel/view.py", "exit_code": 0, "seconds": 0, "output_tail": "opy(p)\n        raise ContractError('missing-port', host, conditions=key)\n\n    def cost(self, path, tag=None):\n        paths = [key for key, n in self.__scene.nodes.items() if (key == path or key.startswith(path + '/')) and (tag is None or (n.capability and tag in n.capability.tags))]\n        occupied = sum(len(self.__scene.reverse.get(key, ())) for key in paths)\n        replaced = sum(len(self.__scene.displaced.get(key, ())) for key in paths)\n        return {'occupied': occupied, 'replaced': replaced, 'instances': len(paths)}\n\n    def offers(self, host, kind=None):\n        return tuple(copy.deepcopy(p) for p in self.__scene.nodes[host].contract.ports if kind is None or p.kind == kind)\n\n    def capability(self, host):\n        return copy.deepcopy(self.__scene.nodes[host].capability)\n\n    def resolved_port(self, host, key):\n        seen = set()\n        while True:\n            if (host, key) in seen:\n                raise ContractError('port-cycle', host, conditions=key)\n            seen.add((host, key))\n            port = self.port(host, key)\n            if not port.delegate:\n                return host, port\n            child, key = port.delegate\n            host = host + '/' + child\n\n    def contract(self, path):\n        if self.__trace:\n            self.__trace.sources[path] = self.__scene.nodes[path].contract.envelope\n        return copy.deepcopy(self.__scene.nodes[path].contract)\n\n    def protected(self, box):\n        if self.__trace:\n            self.__trace.region(box)\n        return any(r.kind in ('clear', 'protected') and any(box.contains(p) for p in r.cells) for n in self.__scene.nodes.values() for r in n.contract.rules)\n\n    def obstacle_distance(self, p, radius=8):\n        best = radius + 1\n        for q in Box(tuple(v - radius for v in p), tuple(v + radius for v in p)).cells():\n            if q in self.__scene.blocks and not passable(self.__scene.blocks[q].state):\n                best = min(best, max(abs(a - b) for a, b in zip(p, q)))\n        return best\n"}
{"timestamp": "2026-09-26T22:23:25.818Z", "command": "python - <<'PY'\nfrom pathlib import Path\nimport hashlib,json,subprocess\nroot=Path('.')\npaths=subprocess.check_output(['rg','--files','hearth','extension_demo','examples','test','-g','*.py'],text=True).splitlines()+['generate.py','ARCHITECTURE.md','GENERATOR.md','pyproject.toml']\nhashes={p:hashlib.sha256(Path(p).read_bytes()).hexdigest() for p in sorted(paths)}\nout=Path('reports/extensions-baseline');out.mkdir(parents=True,exist_ok=False)\n(out/'module-hashes.json').write_text(json.dumps({'phase':'first delivery, before extension phase changes','library_version':'1.1.0','sha256':hashes},indent=2))\n(out/'public-api.md').write_text('''# Public API baseline before extension work\\n\\nVersion 1.1.0. The kernel is unchanged at this snapshot. `module-hashes.json` records source identities.\\n\\n* A component implements `capability()`, `negotiate(Context, parameters)`, and `realize(Context, Contract) -> Plan`. Leaves and composites use this protocol.\\n* `Capability` advertises tags, executable domains, offers, assumptions, adaptation, guarantees and locked inputs. `Contract` holds an envelope, oriented `Port`s, scoped `Grant`s, `Rule`s, decisions and reads.\\n* `Context` offers a read-only world-space `View`, local/world transforms, limits and scoped random streams. View provides states, owners, elevation with a height cap, actual support/water, contract/capability/port discovery and protection/clearance.\\n* `Plan` submits writes, `Child` and `Choice` components and typed relationships. `Binding` grants a named host/port verb within an optional restriction. No component receives the mutable scene store.\\n* `Scene.place/compose_choice/finalize/remove/regenerate` are atomic public operations. Inspection uses `inspect` and exact `cells`, with optional descendants/shared membership.\\n* Rules register with `@validator`. Built-in actual-geometry rules include support, clearance, routes, lighting, enclosure and preservation. Extension validators can read the tentative scene.\\n* Frames support integer translations and horizontal quarter turns. Continuous design geometry must voxelize before submitting effects.\\n* Paired Litematica/JSON transport preserves identities, rules, explicit block properties, NBT and world/export transforms. Reload requires registered extension validators. Generation itself uses portable Python and no filesystem or native geometry engine.\\n\\nProbe policy: first implement diagonal circulation, curve-following boundaries and public-port connectors in `hearth_extensions`, without kernel edits. Record a concrete failing contract test before any general protocol change. This is a self-authored extension exercise, not an independent audit. Browser execution reported by the user is prior external evidence; this phase must not claim new browser execution unless actually run.\\n''')\nprint(f'Baseline saved: {len(hashes)} source/document hashes.')\nPY", "exit_code": 0, "seconds": 0, "output_tail": "Baseline saved: 45 source/document hashes.\n"}
{"timestamp": "2026-09-26T22:23:37.147Z", "command": "cat hearth/components/landscape.py\ncat hearth/environment/fields.py\ncat hearth/environment/terrain.py\ncat test/conftest.py", "exit_code": 0, "seconds": 0, "output_tail": "nt', 'ground'), offers=('construction', 'planting'), guarantees=('frozen field inputs', 'actual 3D substrate'))\n\n    def negotiate(self, ctx, parameters):\n        ports = tuple(Port(k, k, Frame(self.area.lo), self.area, capacity=100000, grant=k) for k in ('construction', 'planting'))\n        grants = (Grant('construction', self.area, ('excavate', 'fill', 'surface'), ('siteworks', 'path', 'pool'), self.edit_limit), Grant('planting', self.area, ('plant',), ('plant',), 0))\n        return Contract(self.area, ports, grants, decisions={'expression': self.field.expression(), 'water': self.water_level, 'edit_limit': self.edit_limit})\n\n    def realize(self, ctx, contract):\n        plan = Plan()\n        x0, bottom, z0 = self.area.lo\n        x1, top, z1 = self.area.hi\n        for x in range(x0, x1 + 1):\n            for z in range(z0, z1 + 1):\n                height = math.floor(self.field(x, z))\n                if not bottom <= height < top:\n                    raise ValueError(f'Terrain field exceeds negotiated vertical domain at {(x,z)}')\n                for y in range(bottom, height + 1):\n                    material = 'stone' if y < height - 2 else ('dirt' if y < height else 'grass_block')\n                    if self.water_level is not None and height < self.water_level:\n                        material = 'gravel' if y == height else material\n                    plan.block((x, y, z), state_of(material), 'strata')\n                if self.water_level is not None:\n                    for y in range(height + 1, self.water_level + 1):\n                        plan.block((x, y, z), state_of('water', level='0'), 'water-volume')\n        return plan\n# Tests keep transient artifacts under test/ and remove them after each test.\nimport sys\nimport tempfile\nfrom pathlib import Path\nimport pytest\n\nsys.path.insert(0, str(Path(__file__).parents[1]))\n\n\n@pytest.fixture\ndef work():\n    with tempfile.TemporaryDirectory(dir=Path(__file__).parent, prefix='tmp-') as name:\n        yield Path(name)\n"}
{"timestamp": "2026-09-26T22:23:37.153Z", "command": "cat hearth/persistence.py\ncat hearth/blocks.py", "exit_code": 0, "seconds": 0, "output_tail": "        raise ValueError('Duplicate block property')\n            props[key] = value\n    return match[1], props\n\n\ndef state_of(name: str, **properties) -> str:\n    if not name.startswith('minecraft:'):\n        name = 'minecraft:' + name\n    return name + ('[' + ','.join(f'{k}={str(v).lower()}' for k, v in sorted(properties.items())) + ']' if properties else '')\n\n\ndef canonical(state: str) -> str:\n    name, props = parse(state)\n    return state_of(name, **props)\n\n\ndef blockstates_equivalent(left: str, right: str) -> bool:\n    return parse(left) == parse(right)\n\n\n@lru_cache(maxsize=4096)\ndef validate_state(state: str):\n    name, props = parse(state)\n    short = name.removeprefix('minecraft:')\n    if short not in SCHEMA:\n        raise ValueError(f'Unknown Java 1.21.1 block: {name}')\n    domain = SCHEMA[short][0]\n    for key, value in props.items():\n        if key not in domain or value not in domain[key]:\n            raise ValueError(f'Invalid property {key}={value} for {name}')\n\n\n@lru_cache(maxsize=4096)\ndef name_of(state: str) -> str:\n    return parse(state)[0].removeprefix('minecraft:')\n\n\ndef solid(state: str) -> bool:\n    name = name_of(state)\n    return name not in SOFT and not any(s in name for s in ('leaves', 'fence', 'pane', 'door', 'lantern', 'chain', 'flower_pot', 'potted_', 'carpet', 'bed', 'button'))\n\n\ndef passable(state: str) -> bool:\n    name = name_of(state)\n    return name in SOFT - {'water'} or name.endswith('_door') or name.endswith('_carpet')\n\n\ndef stairs(material='stone_brick', facing='north', half='bottom'):\n    return state_of(material + '_stairs', facing=facing, half=half, shape='straight', waterlogged='false')\n\n\ndef slab(material='stone_brick', half='bottom'):\n    return state_of(material + '_slab', type=half, waterlogged='false')\n\n\ndef log(material='spruce', axis='y'):\n    return state_of(material + '_log', axis=axis)\n\n\ndef leaves(material='spruce'):\n    return state_of(material + '_leaves', distance='1', persistent='true', waterlogged='false')\n"}
{"timestamp": "2026-09-26T22:23:37.162Z", "command": "sed -n '155,390p' hearth/kernel/scene.py", "exit_code": 0, "seconds": 0, "output_tail": "\n                error = SearchExhausted('search-budget', path, conditions=str(rejected))\n            error.attempts = len(rejected)\n            error.candidates = [d.__dict__ for d in rejected]\n            raise error\n        accepted.sort(key=lambda item: (item[0], item[1]))\n        feasible = [a for a in accepted if a[0] <= accepted[0][0] + choice.tolerance]\n        chosen = scope.child(choice.key).choose(feasible, 'adaptation-selection')\n        self.__dict__.update(chosen[2].__dict__)\n        self.events.append({'kind': 'candidate-selection', 'component': path, 'attempts': len(accepted) + len(rejected), 'accepted': len(accepted), 'rejected': [d.__dict__ for d in rejected], 'scores': [(a[1], round(a[0], 4)) for a in accepted], 'chosen': chosen[1]})\n\n    def _retire(self, path):\n        if self.reverse.get(path):\n            raise ContractError('whole-object', path, conditions='Cannot retire occupied instance')\n        node = self.nodes.get(path)\n        if node:\n            for binding in node.bindings:\n                key = (binding.host, binding.port)\n                self.port_uses[key] = max(0, self.port_uses.get(key, 0) - 1)\n        if node:\n            for shared_host, shared_port, point in node.shared_bindings:\n                key = (shared_host, shared_port)\n                self.port_uses[key] = max(0, self.port_uses.get(key, 0) - 1)\n        self.members.pop(path, None)\n        for children in self.members.values():\n            children.discard(path)\n        self.nodes.pop(path, None)\n        self.reverse.pop(path, None)\n        self._recipes.pop(path, None)\n        self.relations = {r for r in self.relations if r[0] != path and r[2] != path}\n        self.dependencies = [d for d in self.dependencies if d['consumer'] != path and d['source'] != path]\n        self.port_uses = {k: v for k, v in self.port_uses.items() if k[0] != path}\n\n    def _check(self, final, completed_scope=None):\n        errors = validate(self, final, completed_scope)\n        if errors:\n"}
{"timestamp": "2026-09-26T22:24:27.002Z", "command": "cat hearth/components/primitives.py", "exit_code": 0, "seconds": 0, "output_tail": "     p.fill(Box((0, 0, z), (self.width - 1, self.height - 1, z)), self.glass, 'glazing')\n        if self.shutters:\n            from hearth.blocks import slab\n            for x in (-1, self.width):\n                for y in range(self.height):\n                    p.block((x, y, -1), state_of('spruce_trapdoor', facing='north', half='bottom', open='true', powered='false', waterlogged='false'), 'shutters')\n            for x in range(self.width):\n                p.block((x, -1, -1), slab('spruce', 'top'), 'sill')\n        else:\n            for x in range(self.width):\n                p.block((x, self.height - 1, -1), state_of('spruce_trapdoor', facing='north', half='top', open='false', powered='false', waterlogged='false'), 'hood')\n        return p\n\n\n@dataclass(frozen=True)\nclass Pool:\n    width: int = 7\n    depth: int = 6\n\n    def capability(self):\n        return Capability('water.basin', ('pool', 'siteworks'), adaptations=('bounded excavation',), guarantees=('contained water',))\n\n    def negotiate(self, ctx, parameters):\n        box = Box((0, -2, 0), (self.width - 1, 0, self.depth - 1))\n        water = tuple(Box((1, -1, 1), (self.width - 2, 0, self.depth - 2)).cells())\n        return Contract(box, rules=(Rule('pool', water),), decisions={'excavation_limit': box.volume})\n\n    def realize(self, ctx, contract):\n        p = Plan()\n        p.fill(contract.envelope, state_of('smooth_sandstone'), 'basin')\n        p.fill(Box((1, -1, 1), (self.width - 2, 0, self.depth - 2)), state_of('water', level='0'), 'water')\n        return p\n\n\n@dataclass(frozen=True)\nclass Assembly:\n    name: str\n    envelope: Box\n    children: tuple[Child, ...]\n    rules: tuple[Rule, ...] = ()\n    ports: tuple[Port, ...] = ()\n\n    def capability(self):\n        return Capability(self.name, ('composite',))\n\n    def negotiate(self, ctx, parameters):\n        return Contract(self.envelope, ports=self.ports, rules=self.rules)\n\n    def realize(self, ctx, contract):\n        return Plan(children=list(self.children))\n"}
{"timestamp": "2026-09-26T22:26:09.140Z", "command": "sed -n '1,65p' hearth/components/interior.py\nsed -n '1,145p' hearth/components/roofs.py\ncat hearth/kernel/__init__.py", "exit_code": 0, "seconds": 0, "output_tail": "children.append(Child('lantern', Light(), frame=Frame((1, 2, -2))))\n        return p\n\n\n@dataclass(frozen=True)\nclass Gable:\n    span: int\n    steep: bool = True\n\n    def capability(self):\n        return Capability('wall.gable', ('wall', 'enclosure'), offers=('window',), adaptations=('abut existing upper wall',))\n\n    def negotiate(self, ctx, parameters):\n        height = (self.span + 1) // 2\n        region = Box((1, 0, 0), (self.span - 2, height - 2, 0))\n        box = Box((0, -1, -1), (self.span - 1, height - 1, 0))\n        return Contract(box, (Port('infill', 'wall-installation', Frame((1, 0, 0)), region, 4, 'install', facts={'thickness': 1}),), (Grant('install', region, ('install',), ('window',), 30),))\n\n    def realize(self, ctx, contract):\n        p = Plan()\n        for x in range(self.span):\n            step = min(x + 1, self.span - x)\n            y = step if self.steep else (step + 1) // 2\n            for yy in range(y):\n                if name_of(ctx.view.state(ctx.world((x, yy, 0)))) == 'air':\n                    p.block((x, yy, 0), log('spruce') if x in (0, self.span // 2, self.span - 1) else state_of('smooth_sandstone'), 'gable-infill')\n        available = {w.point for w in p.writes}\n        at = (self.span // 2 - 1, 0, 0)\n        if self.steep and all((at[0] + x, y, 0) in available for x in range(2) for y in (0, 1)) and all(name_of(ctx.view.state(ctx.world((at[0] + x, 1, -1)))) == 'air' for x in range(2)):\n            from hearth.kernel import Binding\n            p.children.append(Child('window', Window(2, 2, False), frame=Frame(at), bindings=(Binding(ctx.path, 'infill'),)))\n        return p\n# Public composition kernel, independent of architectural implementations.\nfrom .contracts import Binding, Capability, Child, Choice, Component, Contract, Domain, Grant, Plan, Port, Registry, Rule, Write\nfrom .errors import ContractError, Diagnostic, SearchExhausted, ValidationError\nfrom .scene import Context, Limits, Scene\nfrom .validation import validate, validator\n"}
{"timestamp": "2026-09-26T22:27:46.900Z", "command": "mkdir -p hearth_extensions reports/extension-corpus samples/extensions\ncat > hearth_extensions/geometry.py <<'PY'\n# Portable design geometry and deterministic voxelization shared by extension components.\nfrom dataclasses import dataclass\nimport math\nfrom hearth.space import Box\n\nCARDINAL = ((1, 0), (-1, 0), (0, 1), (0, -1))\n\n\ndef lattice(value):\n    \"\"\"Round consistently across native Python and browser Python.\"\"\"\n    return math.floor(value + 0.5)\n\n\n@dataclass(frozen=True)\nclass Polyline:\n    points: tuple[tuple[float, float], ...]\n\n    def __post_init__(self):\n        object.__setattr__(self, 'points', tuple(tuple(p) for p in self.points))\n        if len(self.points) < 2 or any(len(p) != 2 or not all(math.isfinite(v) for v in p) for p in self.points):\n            raise ValueError('A finite polyline needs at least two two-dimensional points')\n        if any(a == b for a, b in zip(self.points, self.points[1:])):\n            raise ValueError('Repeated consecutive control points')\n\n    @classmethod\n    def bezier(cls, controls, segments=32):\n        if len(controls) != 4 or not 4 <= segments <= 256:\n            raise ValueError('Cubic curves require four controls and 4..256 samples')\n        points = []\n        for i in range(segments + 1):\n            t = i / segments\n            weights = ((1 - t)**3, 3 * t * (1 - t)**2, 3 * t*t * (1 - t), t**3)\n            points.append(tuple(sum(w * p[k] for w, p in zip(weights, controls)) for k in (0, 1)))\n        return cls(tuple(points))\n\n    @property\n    def length(self):\n        return sum(math.dist(a, b) for a, b in zip(self.points, self.points[1:]))\n\n    def nearest(self, point):\n        \"\"\"Return distance, arc length, tangent and projection with stable tie breaks.\"\"\"\n        best, walked = None, 0.0\n        for index, (a, b) in enumerate(zip(self.points, self.points[1:])):\n            dx, dz = b[0] - a[0], b[1] - a[1]\n            length = math.hypot(dx, dz)\n            t = max(0, min(1, ((point[0] - a[0]) * dx + (point[1] - a[1]) * dz) / length**2))\n            q = (a[0] + t * dx, a[1] + t * dz)\n            item = (math.dist(point, q), walked + t * length, index, (dx / length, dz / length), q)\n            if best is None or item[:3] < best[:3]:\n                best = item\n            walked += length\n        return best[0], best[1], best[3], best[4]\n\n    def cells(self):\n        \"\"\"Rasterize a connected cardinal chain, retaining explicit diagonal turns.\"\"\"\n        result = []\n        for a, b in zip(self.points, self.points[1:]):\n            count = max(1, math.ceil(math.dist(a, b) * 4))\n            for i in range(count + 1):\n                target = tuple(lattice(a[k] + (b[k] - a[k]) * i / count) for k in (0, 1))\n                if not result:\n                    result.append(target)\n                while result[-1] != target:\n                    x, z = result[-1]\n                    tx, tz = target\n                    candidates = []\n                    if x != tx:\n                        candidates.append((x + (1 if tx > x else -1), z))\n                    if z != tz:\n                        candidates.append((x, z + (1 if tz > z else -1)))\n                    result.append(min(candidates, key=lambda p: (self.nearest(p)[0], p)))\n        return tuple(result)\n\n    def ribbon(self, radius, caps=False):\n        xmin = math.floor(min(p[0] for p in self.points) - radius)\n        xmax = math.ceil(max(p[0] for p in self.points) + radius)\n        zmin = math.floor(min(p[1] for p in self.points) - radius)\n        zmax = math.ceil(max(p[1] for p in self.points) + radius)\n        a, b = self.points[0], self.points[-1]\n        start = (self.points[1][0] - a[0], self.points[1][1] - a[1])\n        end = (b[0] - self.points[-2][0], b[1] - self.points[-2][1])\n        result = []\n        for x in range(xmin, xmax + 1):\n            for z in range(zmin, zmax + 1):\n                if not caps and ((x-a[0])*start[0] + (z-a[1])*start[1] < 0 or (x-b[0])*end[0] + (z-b[1])*end[1] > 0):\n                    continue\n                if self.nearest((x, z))[0] <= radius + 0.05:\n                    result.append((x, z))\n        return tuple(result)\n\n\n@dataclass(frozen=True)\nclass Polygon:\n    vertices: tuple[tuple[int, int], ...]\n\n    def __post_init__(self):\n        object.__setattr__(self, 'vertices', tuple(tuple(p) for p in self.vertices))\n        if len(self.vertices) < 3:\n            raise ValueError('At least three polygon vertices required')\n\n    @classmethod\n    def chamfered(cls, width, depth, corner=0):\n        if not 9 <= width <= 27 or not 9 <= depth <= 27 or not 0 <= corner <= min(width, depth)//3:\n            raise ValueError('Footprint domain: dimensions 9..27, corner 0..one third')\n        x, z, c = width-1, depth-1, corner\n        return cls(((0,0),(x,0),(x,z),(0,z)) if not c else ((c,0),(x-c,0),(x,c),(x,z-c),(x-c,z),(c,z),(0,z-c),(0,c)))\n\n    def cells(self):\n        result = []\n        for x in range(min(p[0] for p in self.vertices), max(p[0] for p in self.vertices)+1):\n            for z in range(min(p[1] for p in self.vertices), max(p[1] for p in self.vertices)+1):\n                inside = False\n                edge = False\n                for a, b in zip(self.vertices, self.vertices[1:]+self.vertices[:1]):\n                    cross = (x-a[0])*(b[1]-a[1])-(z-a[1])*(b[0]-a[0])\n                    if cross == 0 and min(a[0],b[0]) <= x <= max(a[0],b[0]) and min(a[1],b[1]) <= z <= max(a[1],b[1]):\n                        edge = True\n                    if (a[1] > z) != (b[1] > z) and x < (b[0]-a[0])*(z-a[1])/(b[1]-a[1])+a[0]:\n                        inside = not inside\n                if edge or inside:\n                    result.append((x,z))\n        return tuple(result)\n\n\ndef perimeter(cells, diagonal=False):\n    points = set(cells)\n    steps = tuple((x,z) for x in (-1,0,1) for z in (-1,0,1) if x or z) if diagonal else CARDINAL\n    return tuple(sorted(p for p in points if any((p[0]+dx,p[1]+dz) not in points for dx,dz in steps)))\n\n\ndef bounds2(points, low, high):\n    return Box((min(p[0] for p in points), low, min(p[1] for p in points)), (max(p[0] for p in points), high, max(p[1] for p in points)))\nPY\ncat > hearth_extensions/interfaces.py <<'PY'\n# Public endpoint discovery and orientation checks shared by circulation components.\nfrom dataclasses import dataclass\nfrom hearth import Frame, Binding, ContractError\nfrom hearth.kernel import Capability, Contract, Plan, Rule\n\n\n@dataclass(frozen=True)\nclass Endpoint:\n    host: str\n    port: str\n\n    def binding(self):\n        return Binding(self.host, self.port, 'connect')\n\n    def resolve(self, ctx):\n        port = ctx.view.port(self.host, self.port)\n        if port.kind not in ('access', 'public-access'):\n            raise ContractError('endpoint-kind', ctx.path, port.frame.origin, 'Require an access or public-access port')\n        at = ctx.local(port.frame.origin)\n        turn = (port.frame.turn - ctx.frame.turn) % 4\n        outward = Frame(turn=turn).vector((0,0,-1))\n        return port, at, (outward[0], outward[2])\n\n\n@dataclass(frozen=True)\nclass EndpointContact:\n    endpoint: Endpoint\n\n    def capability(self):\n        return Capability('extension.endpoint-contact', ('connection',), guarantees=('unchanged supported endpoint',))\n\n    def negotiate(self, ctx, parameters):\n        port, at, direction = self.endpoint.resolve(ctx)\n        box = ctx.frame.box(ctx.view.contract(self.endpoint.host).envelope) if False else None\n        from hearth.space import Box\n        return Contract(Box(at, at).expand(1), rules=(Rule('support', ((at[0],at[1]-1,at[2]),)), Rule('clear', (at,(at[0],at[1]+1,at[2])))))\n\n    def realize(self, ctx, contract):\n        return Plan()\nPY\ncat > hearth_extensions/__init__.py <<'PY'\n# Components developed against Hearth's public protocol in the second development phase.\nfrom .geometry import Polyline, Polygon\nfrom .interfaces import Endpoint, EndpointContact\nPY\ncat > test/test_extension_protocol.py <<'PY'\n# A read-only endpoint consumer must not need the host's mutation authority.\nimport pytest\nfrom hearth import Scene, Box\nfrom hearth.kernel import Capability, Contract, Port, Grant, Plan\nfrom hearth.space import Frame\nfrom hearth.blocks import state_of\nfrom hearth_extensions import Endpoint, EndpointContact\n\n\nclass Landing:\n    def capability(self):\n        return Capability('test.landing', ('landing',))\n\n    def negotiate(self, ctx, parameters):\n        return Contract(Box((-1,0,-1),(1,0,1)), ports=(Port('access','access',Frame((0,1,0)),Box((-1,0,-1),(1,2,1)),4,'resurface'),), grants=(Grant('resurface',Box((-1,0,-1),(1,0,1)),('surface',),('path',),9),))\n\n    def realize(self, ctx, contract):\n        p=Plan()\n        p.fill(contract.envelope,state_of('stone_bricks'))\n        return p\n\n\ndef test_read_only_connection_to_mutable_port():\n    s=Scene()\n    s.place('landing',Landing())\n    endpoint=Endpoint('/landing','access')\n    before=dict(s.blocks)\n    s.place('consumer',EndpointContact(endpoint),bindings=(endpoint.binding(),))\n    assert s.blocks==before\n    assert ('/consumer','connected_to','/landing') in s.relations\n    s.finalize()\nPY\npython -m pytest -q test/test_extension_protocol.py > reports/extensions-baseline/failing-connect-probe.txt", "exit_code": 1, "seconds": 0, "output_tail": ""}
{"timestamp": "2026-09-26T22:28:15.038Z", "command": "tail -n 17 reports/extensions-baseline/failing-connect-probe.txt", "exit_code": 0, "seconds": 0, "output_tail": "                raise ContractError('port-capacity', path, port.frame.origin, binding.port)\n            self.port_uses[use_key] = self.port_uses.get(use_key, 0) + 1\n            if binding.verb == 'install':\n                if not port.region.contains(frame.origin):\n                    raise ContractError('port-alignment', path, frame.origin, 'Origin outside installation region')\n                if port.frame.inverse(frame.origin)[0] % port.alignment or frame.turn % 4 != port.frame.turn % 4:\n                    raise ContractError('port-alignment', path, frame.origin, 'Outside facade bay grid')\n            grant = next((g for g in host.contract.grants if g.key == port.grant), None)\n            if grant:\n                if binding.verb not in grant.verbs or (grant.tags and not set(cap.tags).intersection(grant.tags)):\n>                   raise ContractError('permission-kind', path, frame.origin, f'{cap.tags} cannot {binding.verb} {binding.host}')\nE                   hearth.kernel.errors.ContractError: permission-kind: /consumer at (0, 0, 0): ('connection',) cannot connect /landing\n\nhearth/kernel/scene.py:236: ContractError\n=========================== short test summary info ============================\nFAILED test/test_extension_protocol.py::test_read_only_connection_to_mutable_port\n1 failed in 0.06s\n"}
{"timestamp": "2026-09-26T22:28:16.125Z", "command": "cat > reports/extensions-baseline/api-delta.md <<'MD'\n# Protocol delta after the executable connection probe\n\n`test_read_only_connection_to_mutable_port` failed against unchanged Hearth 1.1.0:\n`permission-kind` prevented a consumer from connecting to a landing that also offered a restricted\nresurfacing grant. See `failing-connect-probe.txt`. The extension submitted no writes and required\nactual support/headroom at the public endpoint.\n\nThe general fix is in `Scene._instantiate`: `Binding(..., verb='connect')` now records a read-only\nconnection, capacity use and dependency without acquiring a mutation grant, whether or not the\nport also has a grant. All other verbs retain their original checks. A connection never authorizes\nwriting to the host. The extension cannot implement this using its own effects because binding\npermissions are enforced by the kernel before effects are applied; requesting unrelated mutation\npermission would misrepresent its authority. No component-name dispatch or signature change was added.\n\nThe regression suite adds a negative case proving that a connected consumer cannot overwrite the\nlanding. Old artifact structures and serialized contract schemas are unchanged. This is a self-authored\nextension/protocol exercise, not independent review.\nMD\ncat >> test/test_extension_protocol.py <<'PY'\n\n\ndef test_connection_does_not_acquire_write_authority():\n    from hearth.kernel import ContractError\n    class Bad(EndpointContact):\n        def realize(self, ctx, contract):\n            p=Plan()\n            p.block((0,0,0),state_of('gold_block'))\n            return p\n    s=Scene()\n    s.place('landing',Landing())\n    endpoint=Endpoint('/landing','access')\n    before=dict(s.blocks)\n    with pytest.raises(ContractError,match='write-authority'):\n        s.place('bad',Bad(endpoint),bindings=(endpoint.binding(),))\n    assert s.blocks==before and '/bad' not in s.nodes\nPY\nyapf -i hearth_extensions/geometry.py hearth_extensions/interfaces.py hearth_extensions/__init__.py test/test_extension_protocol.py hearth/kernel/scene.py\npython -m pytest -q test/test_extension_protocol.py test/test_kernel.py test/test_contracts_provenance.py > reports/extension-corpus/kernel-regression.txt", "exit_code": 0, "seconds": 0, "output_tail": ""}
{"timestamp": "2026-09-26T22:31:10.565Z", "command": "cat > hearth_extensions/circulation.py <<'PY'\n# Measured ribbon circulation, support grids and guard geometry through public contracts.\nfrom dataclasses import dataclass\nimport math\nfrom hearth import Box, Frame, Binding, ContractError\nfrom hearth.blocks import state_of, stairs, slab, log, solid, passable, name_of\nfrom hearth.kernel import Capability, Domain, Contract, Port, Grant, Plan, Rule, Child, validator, Diagnostic\nfrom .geometry import Polyline, CARDINAL, bounds2, perimeter, lattice\nfrom .interfaces import Endpoint\n\n\n@validator('extension.guard')\ndef guard_rule(scene, node, rule):\n    for p in rule.cells:\n        if name_of(scene.view.state(p)) not in ('spruce_fence','spruce_log','stone_brick_wall'):\n            yield Diagnostic('bridge-guard',node.path,p,'A required outer guard cell is absent')\n\n\n@validator('extension.span')\ndef span_rule(scene, node, rule):\n    supports=[node.frame.point(tuple(p)) for p in rule.data['anchors']]\n    for p in rule.cells:\n        if not supports or min(abs(p[0]-q[0])+abs(p[2]-q[2]) for q in supports)>rule.data['maximum']:\n            yield Diagnostic('unsupported-span',node.path,p,'Deck exceeds declared supported horizontal span')\n\n\n@dataclass(frozen=True)\nclass Surface:\n    points: tuple\n    role: str = 'deck'\n    material: str = 'spruce_planks'\n\n    def capability(self):\n        return Capability('extension.'+self.role,('path','siteworks',self.role))\n\n    def negotiate(self,ctx,parameters):\n        box=Box.enclosing(self.points)\n        return Contract(box,rules=(Rule('support',self.points),))\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        heights={(x,z):y for x,y,z in self.points}\n        for x,y,z in self.points:\n            uphill=next((face for dx,dz,face in ((1,0,'east'),(-1,0,'west'),(0,1,'south'),(0,-1,'north')) if heights.get((x+dx,z+dz),y)==y+1),None)\n            p.block((x,y,z),stairs('spruce',uphill) if uphill else state_of(self.material),'walk-surface')\n        return p\n\n\n@dataclass(frozen=True)\nclass Trestles:\n    columns: tuple\n    maximum: int = 8\n\n    def capability(self):\n        return Capability('extension.trestles',('siteworks','support'),adaptations=('posts to actual bed','stone shoes'),inputs={'maximum':Domain(2,8)})\n\n    def negotiate(self,ctx,parameters):\n        cells=tuple((x,y,z) for x,bed,z,top,wet in self.columns for y in range(bed-1,top+1))\n        if any(top-bed>self.maximum for x,bed,z,top,wet in self.columns):\n            raise ContractError('connection-support-height',ctx.path,conditions=f'Post exceeds {self.maximum}')\n        return Contract(Box.enclosing(cells),rules=(Rule('support',cells),),decisions={'columns':self.columns})\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        for x,bed,z,top,wet in self.columns:\n            for y in range(bed+1,top+1):\n                p.block((x,y,z),state_of('stone_bricks') if y==bed+1 or not wet else log(),'pier')\n        return p\n\n\n@dataclass(frozen=True)\nclass Guards:\n    points: tuple\n    covered: bool = False\n    roof: tuple = ()\n\n    def capability(self):\n        return Capability('extension.guards',('railing','roof' if self.covered else 'guard'),guarantees=('continuous outer guard',))\n\n    def negotiate(self,ctx,parameters):\n        return Contract(Box.enclosing((*self.points,*self.roof)).expand(1),rules=(Rule('extension.guard',self.points),))\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        occupied={(x,z) for x,y,z in self.points}\n        for i,(x,y,z) in enumerate(self.points):\n            connections={face:str((x+dx,z+dz) in occupied).lower() for dx,dz,face in ((1,0,'east'),(-1,0,'west'),(0,1,'south'),(0,-1,'north'))}\n            p.block((x,y,z),state_of('spruce_fence',waterlogged='false',**connections),'guard')\n            if self.covered:\n                for yy in (y+1,y+2):\n                    p.block((x,yy,z),log() if i%5==0 else state_of('spruce_fence',waterlogged='false',**connections),'gallery-frame')\n        for x,y,z in self.roof:\n            p.block((x,y,z),slab('dark_oak','top'),'gallery-canopy')\n        return p\n\n\n@dataclass(frozen=True)\nclass Link:\n    start: Endpoint\n    end: Endpoint\n    terrain: str\n    via: tuple = ()\n    curve: Polyline | None = None\n    width: int = 3\n    rise: int = 0\n    covered: bool = False\n    max_post: int = 8\n    max_span: int = 6\n    max_length: int = 120\n    fill_limit: int = 800\n\n    def capability(self):\n        return Capability('extension.gallery' if self.covered else 'extension.bridge',('connection','path','composite'),offers=('access',),adaptations=('bounded height profile','actual-bed trestles','supported deck reuse at endpoints'),guarantees=('both public endpoints reachable','two-cell headroom','outer guard'),inputs={'width':Domain(choices=(3,5)),'rise':Domain(0,3),'max_post':Domain(2,8),'max_span':Domain(3,6)},locked=('start','end','via','curve','width','covered'))\n\n    def negotiate(self,ctx,parameters):\n        pa,a,da=self.start.resolve(ctx)\n        pb,b,db=self.end.resolve(ctx)\n        required={(self.start.host,self.start.port),(self.end.host,self.end.port)}\n        if not required.issubset({(v.host,v.port) for v in ctx.bindings if v.verb=='connect'}):\n            raise ContractError('endpoint-binding',ctx.path,conditions='Both endpoints need explicit read-only connection bindings')\n        points=((a[0],a[2]),(a[0]+3*da[0],a[2]+3*da[1]),*self.via,(b[0]+3*db[0],b[2]+3*db[1]),(b[0],b[2]))\n        curve=self.curve or Polyline(tuple(points))\n        if curve.points[0]!=(a[0],a[2]) or curve.points[-1]!=(b[0],b[2]):\n            raise ContractError('endpoint-disconnected',ctx.path,ctx.world(a),'Curve ends must match the negotiated public ports')\n        for point,next_point,direction in ((curve.points[0],curve.points[1],da),(curve.points[-1],curve.points[-2],db)):\n            delta=(next_point[0]-point[0],next_point[1]-point[1])\n            if delta[0]*direction[1]-delta[1]*direction[0]!=0 or delta[0]*direction[0]+delta[1]*direction[1]<=0:\n                raise ContractError('endpoint-frame',ctx.path,ctx.world((int(point[0]),a[1],int(point[1]))),'First and last tangents must point out of the host')\n        center=curve.cells()\n        if len(set(center))!=len(center) or curve.length>self.max_length or curve.length<7:\n            raise ContractError('connection-domain',ctx.path,conditions='Need a simple route of length 7..maximum')\n        radius=self.width//2+1\n        deck=curve.ribbon(radius+.2)\n        station={p:min(range(len(center)),key=lambda i:(math.dist(p,center[i]),i)) for p in deck}\n        count=len(center)\n        heights=[lattice((a[1]-1)*(1-i/(count-1))+(b[1]-1)*i/(count-1)+self.rise*math.sin(math.pi*i/(count-1))) for i in range(count)]\n        beds={}\n        waters={}\n        endpoint_regions=(pa.region,pb.region)\n        for x,z in deck:\n            cap=max(a[1],b[1])+self.rise+2\n            bed=ctx.elevation(x,z,maximum=cap)\n            water=ctx.water(x,z)\n            beds[(x,z)]=bed\n            waters[(x,z)]=water\n            i=station[(x,z)]\n            heights[i]=max(heights[i],bed,water+1 if water is not None else bed)\n        # Minimal upward propagation, preserving fixed endpoint elevations.\n        for i in range(1,count):\n            heights[i]=max(heights[i],heights[i-1]-1)\n        for i in range(count-2,-1,-1):\n            heights[i]=max(heights[i],heights[i+1]-1)\n        if heights[0]!=a[1]-1 or heights[-1]!=b[1]-1:\n            raise ContractError('endpoint-grade',ctx.path,ctx.world(a),'Measured ground/water cannot meet the locked endpoint heights')\n        outer=set(perimeter(deck))\n        guards=[]\n        clear=[]\n        all_deck=[]\n        writes=[]\n        for x,z in deck:\n            i=station[(x,z)]\n            y=heights[i]\n            q=ctx.world((x,y,z))\n            host_region=any(region.contains(q) for region in endpoint_regions)\n            is_guard=(x,z) in outer and 2<i<count-3 and not host_region\n            all_deck.append((x,y,z))\n            if is_guard:\n                guards.append((x,y+1,z))\n            else:\n                clear.extend(((x,y+1,z),(x,y+2,z)))\n            existing=ctx.view.state(q)\n            if solid(existing) and host_region:\n                continue\n            writes.append((x,y,z))\n        anchors=[]\n        for x,z in sorted(deck,key=lambda p:(station[p],p)):\n            if any(abs(x-u)+abs(z-v)<=self.max_span for u,v in anchors):\n                continue\n            # Prefer the central route where possible, then cover wide diagonal corners.\n            near=center[station[(x,z)]]\n            anchor=near if near in beds else (x,z)\n            if anchor in anchors:\n                anchor=(x,z)\n            anchors.append(anchor)\n        columns=[]\n        for x,z in anchors:\n            top=heights[station[(x,z)]]-1\n            bed=min(beds[(x,z)],top)\n            while not ctx.view.supports(ctx.world((x,bed,z))):\n                bed-=1\n                if top-bed>self.max_post:\n                    raise ContractError('connection-support-height',ctx.path,ctx.world((x,top,z)))\n            if not ctx.view.supports(ctx.world((x,bed-1,z))):\n                raise ContractError('connection-bed',ctx.path,ctx.world((x,bed-1,z)),'Need two actual supporting bed cells')\n            columns.append((x,bed,z,top,waters[(x,z)] is not None))\n        fill=sum(max(0,top-bed) for x,bed,z,top,wet in columns)\n        if fill>self.fill_limit:\n            raise ContractError('connection-fill-limit',ctx.path,conditions=f'{fill}>{self.fill_limit}')\n        roof=tuple((x,y+5,z) for x,y,z in all_deck if self.covered and 3<station[(x,z)]<count-4)\n        guard_points=tuple(guards)\n        low=min(min(beds.values())-1,min(heights))\n        envelope=bounds2(deck,low,max(heights)+7).expand(1)\n        center_route=tuple((x,heights[i]+1,z) for i,(x,z) in enumerate(center))\n        rules=(Rule('route',(a,*center_route,b),envelope,phase='complete'),Rule('support',tuple(all_deck)),Rule('clear',tuple(clear)),Rule('extension.span',tuple(all_deck),data={'anchors':[(x,0,z) for x,z in anchors],'maximum':self.max_span}))\n        return Contract(envelope,ports=(Port('start','access',Frame(a),Box(a,a),8),Port('end','access',Frame(b),Box(b,b),8)),rules=rules,decisions={'curve':curve.points,'center':center,'profile':heights,'deck':all_deck,'writes':writes,'guards':guard_points,'columns':columns,'roof':roof,'fill':fill,'width':self.width,'covered':self.covered,'rise':self.rise,'endpoint_regions':[[r.lo,r.hi] for r in endpoint_regions]},reads=(envelope,))\n\n    def realize(self,ctx,contract):\n        d=contract.decisions\n        terrain=(Binding(self.terrain,'construction','fill',ctx.frame.box(contract.envelope)),)\n        p=Plan(children=[Child('supports',Trestles(tuple(d['columns']),self.max_post),bindings=terrain),Child('deck',Surface(tuple(d['writes'])),bindings=terrain),Child('guards',Guards(tuple(d['guards']),self.covered,tuple(d['roof'])))])\n        p.relations.extend(((ctx.path,'connects',self.start.host),(ctx.path,'connects',self.end.host)))\n        return p\n\n\ndef connection(key,start,end,terrain,**parameters):\n    \"\"\"Create a fully bound link through public endpoint references.\"\"\"\n    return Child(key,Link(start,end,terrain,**parameters),bindings=(start.binding(),end.binding()))\nPY\ncat > hearth_extensions/boundaries.py <<'PY'\n# Curve-following masonry with sampled installation sockets and grounded adaptation.\nfrom dataclasses import dataclass\nfrom hearth import Box,Frame,Binding,ContractError\nfrom hearth.kernel import Capability,Contract,Domain,Port,Grant,Plan,Rule,Child\nfrom hearth.blocks import state_of,slab\nfrom .geometry import Polyline,bounds2\n\n\n@dataclass(frozen=True)\nclass Boundary:\n    curve: Polyline\n    height: int = 2\n    spacing: int = 7\n    max_step: int = 1\n\n    def capability(self):\n        return Capability('extension.curved-boundary',('boundary','siteworks','composite'),offers=('mount','end'),inputs={'height':Domain(1,5),'spacing':Domain(4,12)},adaptations=('terrain-following base','stepped coping'),guarantees=('continuous cardinal contour','grounded boundary','mount sockets'))\n\n    def negotiate(self,ctx,parameters):\n        center=self.curve.cells()\n        if len(center)>220 or len(set(center))!=len(center):\n            raise ContractError('boundary-domain',ctx.path,conditions='Simple open contour, at most 220 raster cells')\n        levels=[ctx.elevation(x,z) for x,z in center]\n        if any(abs(a-b)>self.max_step for a,b in zip(levels,levels[1:])):\n            raise ContractError('boundary-grade',ctx.path,conditions='Adjacent wall foundations exceed step bound')\n        ports=[]\n        grants=[]\n        for i in range(2,len(center)-2,self.spacing):\n            x,z=center[i]\n            y=levels[i]+self.height+2\n            x0,z0=center[max(0,i-1)];x1,z1=center[min(len(center)-1,i+1)]\n            turn=0 if abs(x1-x0)>=abs(z1-z0) else 1\n            at=(x,y,z)\n            key=f'mount-{i}'\n            box=Box(at,at)\n            ports.append(Port(key,'mount',Frame(at,turn),box,1,key,facts={'support':(x,y-1,z),'load':'small-fixture'}))\n            grants.append(Grant(key,box,('install',),('fixture',),0))\n        supports=tuple((x,levels[i],z) for i,(x,z) in enumerate(center))\n        shell=tuple((x,y,z) for i,(x,z) in enumerate(center) for y in range(levels[i]+1,levels[i]+self.height+2))\n        box=Box.enclosing((*supports,*shell)).expand(2)\n        return Contract(box,tuple(ports),tuple(grants),(Rule('support',supports),Rule('sealed',shell)),decisions={'center':center,'levels':levels,'height':self.height},reads=(box,))\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        d=contract.decisions\n        rng=ctx.rng('masonry')\n        for i,(x,z) in enumerate(d['center']):\n            for y in range(d['levels'][i]+1,d['levels'][i]+self.height+1):\n                p.block((x,y,z),state_of(rng.choice(('stone_bricks','stone_bricks','mossy_stone_bricks'))),'contour-masonry')\n            p.block((x,d['levels'][i]+self.height+1,z),slab('stone_brick','top'),'coping')\n        return p\n\n\n@dataclass(frozen=True)\nclass BoundaryLantern:\n    def capability(self):\n        return Capability('extension.boundary-lantern',('fixture','light'),guarantees=('mounted actual light',))\n\n    def negotiate(self,ctx,parameters):\n        return Contract(Box((0,0,0),(0,1,0)),rules=(Rule('support',((0,-1,0),)),Rule('expected',((0,0,0),),data={'names':['lantern']})),atomic_object=True)\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        p.block((0,0,0),state_of('lantern',hanging='false',waterlogged='false'),'lamp')\n        return p\n\n\n@dataclass(frozen=True)\nclass IlluminateBoundary:\n    host: str\n    every: int = 1\n\n    def capability(self):\n        return Capability('extension.boundary-lighting',('composite',))\n\n    def negotiate(self,ctx,parameters):\n        ports=ctx.view.offers(self.host,'mount')[::self.every]\n        if not ports:\n            raise ContractError('missing-mounts',ctx.path)\n        return Contract(Box.enclosing(ctx.local(p.frame.origin) for p in ports).expand(1))\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        for port in ctx.view.offers(self.host,'mount')[::self.every]:\n            frame=Frame(ctx.local(port.frame.origin),(port.frame.turn-ctx.frame.turn)%4)\n            p.children.append(Child(port.key,BoundaryLantern(),frame=frame,bindings=(Binding(self.host,port.key),)))\n        return p\nPY\nyapf -i hearth_extensions/circulation.py hearth_extensions/boundaries.py", "exit_code": 0, "seconds": 0, "output_tail": ""}
{"timestamp": "2026-09-26T22:32:21.385Z", "command": "cat > hearth_extensions/sites.py <<'PY'\n# Accessible platforms with measured supports and oriented edge ports.\nfrom dataclasses import dataclass\nfrom hearth import Box,Frame,Binding,ContractError\nfrom hearth.kernel import Capability,Contract,Plan,Child,Port,Rule,Domain\nfrom hearth.blocks import state_of\nfrom hearth.adapters.site import Foundation,SiteLimits,survey\nfrom .geometry import Polygon,bounds2\nfrom .circulation import Surface\n\nDIRECTIONS={'north':(0,-1,0),'east':(1,0,1),'south':(0,1,2),'west':(-1,0,3)}\n\n\ndef edge_frames(width,depth,height):\n    return {'north':Frame((width//2,height,0),0),'east':Frame((width-1,height,depth//2),1),'south':Frame((width//2,height,depth-1),2),'west':Frame((0,height,depth//2),3)}\n\n\n@dataclass(frozen=True)\nclass Platform:\n    terrain: str\n    width: int = 9\n    depth: int = 9\n    freeboard: int = 1\n\n    def capability(self):\n        return Capability('extension.platform',('platform','composite','habitable'),offers=('access',),inputs={'width':Domain(5,27),'depth':Domain(5,27),'freeboard':Domain(1,3)})\n\n    def negotiate(self,ctx,parameters):\n        footprint=tuple((x,z) for x in range(self.width) for z in range(self.depth))\n        samples=survey(ctx,footprint)\n        y=samples['floor']+self.freeboard-1\n        region=bounds2(footprint,min(v[0] for v in samples['samples'].values())-1,y+3)\n        ports=tuple(Port(k,'access',f,f.box(Box((-2,-1,0),(2,1,0))),8) for k,f in edge_frames(self.width,self.depth,y+1).items())\n        return Contract(region,ports=ports,decisions={'floor':y,'footprint':footprint,'strategy':samples['strategy']},rules=(Rule('route',tuple(f.frame.origin for f in ports),region,phase='complete'),))\n\n    def realize(self,ctx,contract):\n        d=contract.decisions\n        p=Plan(children=[Child('foundation',Foundation(tuple(d['footprint']),d['floor'],d['strategy']),bindings=(Binding(self.terrain,'construction','fill'),)),Child('deck',Surface(tuple((x,d['floor'],z) for x,z in d['footprint']),role='platform'))])\n        return p\nPY\ncat >> hearth_extensions/__init__.py <<'PY'\nfrom .circulation import Link, connection\nfrom .boundaries import Boundary, BoundaryLantern, IlluminateBoundary\nfrom .sites import Platform\nPY\ncat > test/test_extension_geometry.py <<'PY'\n# Extension-only probes for non-cardinal circulation and curve interfaces.\nfrom dataclasses import replace\nimport pytest\nfrom hearth import Scene,Box,Frame,Binding,ContractError\nfrom hearth.kernel import validate,Child,Plan\nfrom hearth.components.primitives import Volume\nfrom hearth.environment import Terrain,gradient,wave\nfrom hearth.blocks import state_of\nfrom hearth.persistence import export_scene,load_scene,semantic_digest\nfrom hearth_extensions import Polyline,Endpoint,Link,Boundary,IlluminateBoundary,Platform\n\n\ndef bridge_scene(seed=0,water=3):\n    s=Scene(seed,domain=Box((-8,-4,-8),(45,42,45)))\n    s.place('land',Terrain(s.domain,gradient(.02,.01,1)+wave(.2,.1,.15,seed),water))\n    s.place('a',Platform('/land'),frame=Frame((0,0,0)))\n    s.place('b',Platform('/land'),frame=Frame((18,0,27)))\n    a,b=Endpoint('/a','south'),Endpoint('/b','north')\n    s.place('link',Link(a,b,'/land',rise=1),bindings=(a.binding(),b.binding()))\n    return s.finalize()\n\n\ndef test_diagonal_bridge_actual_route_support_railings_and_reload(work):\n    s=bridge_scene()\n    assert any(n.type=='extension.trestles' for n in s.nodes.values())\n    assert len(set(x for x,z in s.nodes['/link'].contract.decisions['center']))>5\n    assert not validate(s,True)\n    p=next(iter(s.cells('/link/deck')))\n    q=s.inspect(p)\n    assert any('connect' in str(r) for r in s.inspect(p)['related']) or ('/link','connects','/a') in s.relations\n    export_scene(s,work/'diagonal bridge.litematic')\n    t=load_scene(work/'diagonal bridge.litematic')\n    assert t.inspect(p)==q and semantic_digest(t)==semantic_digest(s)\n\n\ndef test_curve_boundary_interfaces_and_external_attachment():\n    s=Scene(3,domain=Box((-5,-4,-5),(45,35,35)))\n    s.place('land',Terrain(s.domain,gradient(.015,.01,3)))\n    curve=Polyline.bezier(((0,0),(7,25),(28,-3),(37,20)))\n    s.place('boundary',Boundary(curve))\n    s.place('lamps',IlluminateBoundary('/boundary'))\n    s.finalize()\n    ports=s.view.offers('/boundary','mount')\n    assert len(ports)>3\n    p=ports[0].frame.origin\n    q=s.inspect(p)\n    assert q['chain'][1]['instance']=='/boundary'\n    assert p in s.cells('/boundary',descendants=True)\nPY\nyapf -i hearth_extensions/sites.py hearth_extensions/__init__.py test/test_extension_geometry.py\npython -m pytest -q test/test_extension_geometry.py > reports/extension-corpus/first-geometry-probes.txt", "exit_code": 1, "seconds": 1, "output_tail": ""}
{"timestamp": "2026-09-26T22:32:34.160Z", "command": "tail -n 55 reports/extension-corpus/first-geometry-probes.txt\ncat reports/extension-corpus/kernel-regression.txt", "exit_code": 0, "seconds": 0, "output_tail": ".F                                                                       [100%]\n=================================== FAILURES ===================================\n____________ test_curve_boundary_interfaces_and_external_attachment ____________\n\n    def test_curve_boundary_interfaces_and_external_attachment():\n        s = Scene(3, domain=Box((-5, -4, -5), (45, 35, 35)))\n        s.place('land', Terrain(s.domain, gradient(.015, .01, 3)))\n        curve = Polyline.bezier(((0, 0), (7, 25), (28, -3), (37, 20)))\n        s.place('boundary', Boundary(curve))\n        s.place('lamps', IlluminateBoundary('/boundary'))\n        s.finalize()\n        ports = s.view.offers('/boundary', 'mount')\n        assert len(ports) > 3\n        p = ports[0].frame.origin\n        q = s.inspect(p)\n>       assert q['chain'][1]['instance'] == '/boundary'\n               ^^^^^^^^^^^^^^^^^^^^^^^^^\nE       KeyError: 'instance'\n\ntest/test_extension_geometry.py:47: KeyError\n=========================== short test summary info ============================\nFAILED test/test_extension_geometry.py::test_curve_boundary_interfaces_and_external_attachment\n1 failed, 1 passed in 1.47s\n...........................                                              [100%]\n27 passed in 0.52s\n"}
{"timestamp": "2026-09-26T22:32:43.640Z", "command": "sed -n '395,449p' hearth/kernel/scene.py\nsed -n '1,130p' test/test_kernel.py", "exit_code": 0, "seconds": 0, "output_tail": "xis='z')\n    path = work / 'space name.litematic'\n    export_scene(s, path)\n    restored = load_scene(path)\n    assert restored.inspect((14, 3, -5)) == s.inspect((14, 3, -5))\n    assert blockstates_equivalent('minecraft:oak_stairs[facing=north,half=bottom]', 'minecraft:oak_stairs[half=bottom,facing=north]')\n    assert not blockstates_equivalent('minecraft:oak_log', 'minecraft:oak_log[axis=y]')\n    import json\n    meta = path.with_suffix('.litematic.structure.json')\n    data = json.loads(meta.read_text())\n    data['digest'] = 'bad'\n    meta.write_text(json.dumps(data))\n    with pytest.raises(ContractError, match='geometry-mismatch'):\n        load_scene(path)\n\n\ndef test_clearance_rollback_and_bounds():\n    s = Scene()\n    s.place('stairs', Assembly('circulation', Box((0, 0, 0), (3, 5, 3)), (), (Rule('clear', ((1, 2, 1), (1, 3, 1))),)))\n    with pytest.raises(ContractError, match='clearance'):\n        s.place('decoration', Volume(Box((1, 3, 1), (1, 3, 1)), state_of('stone')))\n    assert set(s.nodes) == {'/stairs'} and not s.blocks\n\n    @dataclass\n    class Escaper:\n\n        def capability(self):\n            return Capability('bad', ())\n\n        def negotiate(self, c, p):\n            return Contract(Box((0, 0, 0), (0, 0, 0)))\n\n        def realize(self, c, b):\n            p = Plan()\n            p.block((1, 0, 0), state_of('stone'))\n            return p\n\n    with pytest.raises(ContractError, match='envelope'):\n        s.place('bad', Escaper())\n\n\ndef test_seed_stability_and_regeneration():\n    from hearth.randomness import Scope\n    a = Scope(123).child('house')\n    before = a.stream('layout').getrandbits(64)\n    Scope(123).child('failed').stream().random()\n    assert before == a.stream('layout').getrandbits(64)\n    s = Scene()\n    s.place('beam', Volume(Box((0, 0, 0), (2, 0, 0)), state_of('stone')))\n    s.regenerate('/beam', Volume(Box((0, 0, 0), (1, 0, 0)), state_of('oak_planks')))\n    assert s.inspect((2, 0, 0))['instance'] is None\n    assert len(s.cells('/beam')) == 2\n"}
{"timestamp": "2026-09-26T22:35:40.663Z", "command": "cat > reports/extension-corpus/sampling-policy.json <<'JSON'\n{\n  \"phase\": \"extension\", \"version\": 1, \"root_seed\": 0,\n  \"frozen_before_assessment\": true,\n  \"connectors\": {\"environment_seeds\": [0,7], \"structure_seeds\": [0,5], \"count\": 48, \"policy\": \"Eight independently frozen generic terrain expressions, each copied for six width/rise/cover/curve decisions. Locked platforms and public endpoints. Gradients bounded by 0.035, waves 0..0.7, radial depression 0..2, optional water level 3. Width 3 or 5, rise 0..2, post height <=8, maximum unsupported taxicab deck span 6, fill <=800. No resampling.\"},\n  \"boundaries\": {\"count\": 16, \"policy\": \"Four environment seeds by four independently seeded cubic controls, height 1..3 and socket spacing 4..10. Include a zero-curvature line. Maximum base change one block per raster step.\"},\n  \"halls\": {\"count\": 12, \"policy\": \"Seeds 0..11, variable polygon widths/depths 13..25, chamfer 0..4, ceiling 5..7, hip or hip with lantern. Functions, entrances and proportions locked before site query.\"},\n  \"compositions\": {\"count\": 12, \"policy\": \"Two lightweight public graph clients, seeds 0..5. One branching campus, one perimeter cluster. Preserve locked vertices and declared graph edges. Includes existing Hearth buildings substituted for polygon halls.\"},\n  \"stress\": {\"count\": 24, \"policy\": \"Single locked endpoint pair per input. Independent environment seeds 2000..2023, gradients through 0.28, waves through 4, water through 12, profile rise 0..3. Retain every success and failure without resampling or widened bounds.\"},\n  \"novel_compositions\": \"Focused tests additionally use an unsampled kinked polyline and a polygon footprint not used in the two shipped clients.\",\n  \"metrics\": [\"input outcome\", \"candidate acceptance\", \"all diagnostics\", \"runtime\", \"actual edit cost\", \"principal geometry/topology signature\", \"contact signature\"],\n  \"claims\": \"Self-authored finite extension exercise, not independent audit. Prior external Pyodide results are user-reported; this phase claims only execution actually performed here.\"\n}\nJSON\npython - <<'PY'\nfrom pathlib import Path\np=Path('test/test_extension_geometry.py');s=p.read_text().replace(\"q['chain'][1]['instance']\", \"q['chain'][1]['id']\");p.write_text(s)\nPY\ncat > hearth_extensions/halls.py <<'PY'\n# Polygon halls, public facade installation and concentric hipped roof components.\nfrom dataclasses import dataclass\nfrom hearth import Box,Frame,Binding,ContractError\nfrom hearth.kernel import Capability,Contract,Port,Grant,Rule,Plan,Child,Domain,Context\nfrom hearth.blocks import state_of,log,slab,stairs\nfrom hearth.adapters.site import survey,Foundation,Approach,SiteLimits\nfrom hearth.components.interior import Door,Light,Furniture\nfrom hearth.components.primitives import Window\nfrom .geometry import Polygon,CARDINAL,bounds2,perimeter\nfrom .sites import edge_frames\nfrom .circulation import Surface\n\n\n@dataclass(frozen=True)\nclass PolygonShell:\n    footprint: Polygon\n    height: int = 5\n    entrances: tuple = ('north','south')\n\n    def capability(self):\n        return Capability('extension.polygon-shell',('wall','enclosure'),offers=('wall-installation',),guarantees=('continuous enclosure','protected floor support'),inputs={'height':Domain(5,7)})\n\n    def negotiate(self,ctx,parameters):\n        footprint=self.footprint.cells()\n        shell=perimeter(footprint,diagonal=True)\n        xs=[x for x,z in footprint];zs=[z for x,z in footprint]\n        width,depth=max(xs)+1,max(zs)+1\n        if min(xs)!=0 or min(zs)!=0:\n            raise ContractError('polygon-origin',ctx.path,conditions='Normalize footprint minimum to (0,0)')\n        frames=edge_frames(width,depth,0)\n        ports=[];grants=[]\n        for side,f in frames.items():\n            for role,offset,size,base in [('entry',0,1,0)] if side in self.entrances else []:\n                at=f.compose(Frame((offset,base,0)))\n                region=at.box(Box((0,0,0),(size-1,2,0)))\n                if not all((p[0],p[2]) in shell for p in region.cells()):\n                    raise ContractError('polygon-entry',ctx.path,at.origin,'Requested cardinal entry is not on the footprint boundary')\n                key='entry-'+side\n                ports.append(Port(key,'wall-installation',at,region,1,key,facts={'thickness':1,'role':role,'side':side}))\n                grants.append(Grant(key,region,('install',),('door',),3))\n            for i,offset in enumerate((-4,2)):\n                at=f.compose(Frame((offset,1,0)))\n                region=at.box(Box((0,0,0),(1,1,0)))\n                if all((p[0],p[2]) in shell for p in region.cells()):\n                    key=f'window-{side}-{i}'\n                    ports.append(Port(key,'wall-installation',at,region,1,key,facts={'thickness':1,'role':'window','side':side}))\n                    grants.append(Grant(key,region,('install',),('window',),4))\n        blocks=tuple((x,y,z) for x,z in shell for y in range(self.height))\n        return Contract(bounds2(footprint,0,self.height-1),tuple(ports),tuple(grants),rules=(Rule('sealed',blocks),Rule('support',tuple((x,-1,z) for x,z in shell))),decisions={'shell':shell,'height':self.height})\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        vertices=set(self.footprint.vertices)\n        for x,z in contract.decisions['shell']:\n            for y in range(self.height):\n                frame=(x,z) in vertices or y==self.height-1\n                p.block((x,y,z),log() if frame else state_of('smooth_sandstone'),'frame' if frame else 'infill')\n        return p\n\n\n@dataclass(frozen=True)\nclass Facade:\n    host: str\n    shutters: bool = True\n\n    def capability(self):\n        return Capability('extension.facade-installations',('composite',))\n\n    def negotiate(self,ctx,parameters):\n        box=ctx.view.contract(self.host).envelope\n        return Contract(Box.enclosing(ctx.local(p) for p in box.corners()).expand(2))\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        for port in ctx.view.offers(self.host,'wall-installation'):\n            component=Door() if port.facts['role']=='entry' else Window(shutters=self.shutters)\n            frame=Frame(ctx.local(port.frame.origin),(port.frame.turn-ctx.frame.turn)%4)\n            p.children.append(Child(port.key,component,frame=frame,bindings=(Binding(self.host,port.key),)))\n        return p\n\n\n@dataclass(frozen=True)\nclass HipRoof:\n    footprint: Polygon\n    run: int = 2\n    lantern: bool = False\n    material: str = 'deepslate_tile'\n\n    def capability(self):\n        return Capability('extension.hip-roof',('roof','composite'),adaptations=('polygon inset courses',),inputs={'run':Domain(choices=(1,2,3))},guarantees=('continuous concentric weather surface',))\n\n    def negotiate(self,ctx,parameters):\n        points=set(self.footprint.cells())\n        points|={(x+dx,z+dz) for x,z in tuple(points) for dx,dz in CARDINAL}\n        remaining=set(points)\n        depths={}\n        depth=0\n        while remaining:\n            ring=perimeter(remaining)\n            for p in ring:\n                depths[p]=depth\n            remaining-=set(ring)\n            depth+=1\n        courses=tuple((x,depths[(x,z)]//self.run,z) for x,z in sorted(points))\n        peak=max(y for x,y,z in courses)\n        cx=(min(x for x,z in points)+max(x for x,z in points))//2\n        cz=(min(z for x,z in points)+max(z for x,z in points))//2\n        return Contract(bounds2(points,-1,peak+7),rules=(Rule('sealed',courses),),decisions={'courses':courses,'peak':peak,'center':(cx,cz),'run':self.run,'lantern':self.lantern})\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        d=contract.decisions\n        heights={(x,z):y for x,y,z in d['courses']}\n        for x,y,z in d['courses']:\n            face=next((f for dx,dz,f in ((1,0,'east'),(-1,0,'west'),(0,1,'south'),(0,-1,'north')) if heights.get((x+dx,z+dz),y)>y),None)\n            material='dark_oak' if y==0 else self.material\n            p.block((x,y,z),stairs(material,face) if face else slab(material,'top'),'hip-course')\n        if self.lantern:\n            cx,cz=d['center']\n            p.children.append(Child('lantern',RoofLantern(),frame=Frame((cx-2,d['peak']+1,cz-2))))\n        return p\n\n\n@dataclass(frozen=True)\nclass RoofLantern:\n    def capability(self):\n        return Capability('extension.roof-lantern',('roof','daylight','composite'))\n\n    def negotiate(self,ctx,parameters):\n        return Contract(Box((-1,0,-1),(5,5,5)))\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        for x in range(5):\n            for z in range(5):\n                p.block((x,0,z),state_of('spruce_planks'),'ring-beam')\n                if x in (0,4) or z in (0,4):\n                    for y in (1,2):\n                        p.block((x,y,z),log() if x in (0,4) and z in (0,4) else state_of('glass'),'clerestory')\n        p.children.append(Child('cap',HipRoof(Polygon.chamfered(9,9),run=2),frame=Frame((-2,3,-2))))\n        return p\n\n\n@dataclass(frozen=True)\nclass Pavilion:\n    footprint: Polygon\n    terrain: str\n    purpose: str = 'library'\n    entrances: tuple = ('north','south')\n    height: int = 5\n    roof_run: int = 2\n    lantern: bool = False\n    limits: SiteLimits = SiteLimits()\n\n    def capability(self):\n        return Capability('extension.pavilion',('building','habitable','composite'),offers=('access',),adaptations=('polygon support grid','independently measured approaches'),guarantees=('connected entrances','furnished lit enclosed hall'),inputs={'height':Domain(5,7),'purpose':Domain(choices=('living','library','workshop','kitchen','bedroom','storage'))},locked=('footprint','purpose','entrances','height','roof_run','lantern'))\n\n    def negotiate(self,ctx,parameters):\n        points=self.footprint.cells()\n        width=max(x for x,z in points)+1\n        depth=max(z for x,z in points)+1\n        if not 13<=width<=25 or not 13<=depth<=25 or not self.entrances:\n            raise ContractError('hall-domain',ctx.path,conditions='Footprints 13..25, at least one declared entrance')\n        measured=survey(ctx,points,self.limits)\n        floor=measured['floor']\n        ports=[];approaches=[]\n        frames=edge_frames(width,depth,0)\n        low=min(v[0] for v in measured['samples'].values())-1\n        boxes=[bounds2(points,low,floor+self.height+23).expand(3)]\n        targets=[(width//2,floor+1,depth//2)]\n        for side in self.entrances:\n            if side not in frames:\n                raise ContractError('hall-entrance',ctx.path,conditions=side)\n            f=frames[side]\n            comp=Approach(0,floor,limits=self.limits)\n            ac=comp.negotiate(Context(ctx.path,ctx.frame.compose(f),ctx.scope.child('approach-'+side),ctx.view,(),ctx.limits),{})\n            local=ac.transformed(f)\n            port=local.ports[0]\n            ports.append(Port(side,'access',Frame(port.frame.origin,f.turn),port.region,8,delegate=('approach-'+side,'street')))\n            approaches.append((side,f,comp))\n            boxes.append(local.envelope)\n            targets.extend(((f.origin[0],floor+1,f.origin[2]),port.frame.origin))\n        envelope=Box.enclosing(p for box in boxes for p in box.corners())\n        interior=Box((1,floor+1,1),(width-2,floor+self.height-1,depth-2))\n        groups={'library':[['bookshelf'],['lectern']],'workshop':[['crafting_table'],['smithing_table']],'kitchen':[['smoker'],['furnace']],'bedroom':[['red_bed']],'living':[['spruce_stairs']],'storage':[['barrel']]}\n        return Contract(envelope,tuple(ports),rules=(Rule('route',tuple(targets),envelope,phase='complete'),Rule('light',tuple(targets[:1]),interior,{'minimum':1},'complete'),Rule('furnishing',box=interior,data={'groups':groups[self.purpose]},phase='complete')),decisions={'floor':floor,'width':width,'depth':depth,'footprint':points,'vertices':self.footprint.vertices,'strategy':measured['strategy'],'entrances':self.entrances,'height':self.height,'purpose':self.purpose,'roof_run':self.roof_run,'lantern':self.lantern})\n\n    def realize(self,ctx,contract):\n        d=contract.decisions;floor=d['floor'];w=d['width'];depth=d['depth']\n        p=Plan(children=[Child('foundation',Foundation(tuple(d['footprint']),floor,d['strategy'],self.limits),bindings=(Binding(self.terrain,'construction','fill'),)),Child('floor',Surface(tuple((x,floor,z) for x,z in d['footprint']),role='hall-floor')),Child('ceiling',Surface(tuple((x,floor+self.height+1,z) for x,z in d['footprint']),role='hall-ceiling')),Child('shell',PolygonShell(self.footprint,self.height,self.entrances),frame=Frame((0,floor+1,0))),Child('facade',Facade(ctx.path+'/shell'))])\n        for side in self.entrances:\n            f=edge_frames(w,depth,0)[side]\n            p.children.append(Child('approach-'+side,Approach(0,floor,limits=self.limits),frame=f,bindings=(Binding(self.terrain,'construction','surface'),)))\n        p.children.append(Child('furnishings',Furniture(self.purpose,w-6,depth-6),frame=Frame((3,floor+1,3))))\n        for x in range(4,w-3,5):\n            for z in range(4,depth-3,5):\n                p.children.append(Child(f'light-{x}-{z}',Light(),frame=Frame((x,floor+self.height-1,z))))\n        p.children.append(Child('roof',HipRoof(self.footprint,self.roof_run,self.lantern),frame=Frame((0,floor+self.height+2,0))))\n        return p\nPY\nyapf -i hearth_extensions/halls.py test/test_extension_geometry.py", "exit_code": 0, "seconds": 0, "output_tail": ""}
{"timestamp": "2026-09-26T22:36:49.549Z", "command": "cat >> hearth_extensions/__init__.py <<'PY'\nfrom .halls import Pavilion, PolygonShell, HipRoof, Facade\nPY\ncat >> test/test_extension_geometry.py <<'PY'\n\n\n@pytest.mark.parametrize('width,depth,corner,lantern',[(13,17,0,False),(17,21,3,True),(25,13,4,False)])\ndef test_polygon_hall_multiple_usable_ports(width,depth,corner,lantern,work):\n    from hearth_extensions import Pavilion,Polygon\n    s=Scene(2,domain=Box((-15,-4,-15),(42,60,42)))\n    s.place('land',Terrain(s.domain,gradient(.025,-.018,4)))\n    s.place('hall',Pavilion(Polygon.chamfered(width,depth,corner),'/land',entrances=('north','east','south','west'),lantern=lantern))\n    s.finalize()\n    assert len(s.view.offers('/hall','access'))==4\n    glass=next(p for p,c in s.blocks.items() if c.state=='minecraft:glass' and '/facade/' in c.owner)\n    assert [n['type'] for n in s.inspect(glass)['chain']][:3]==['window.shuttered','extension.polygon-shell','extension.pavilion']\n    export_scene(s,work/'hall.litematic')\n    assert load_scene(work/'hall.litematic').inspect(glass)==s.inspect(glass)\nPY\nyapf -i hearth_extensions/halls.py hearth_extensions/__init__.py test/test_extension_geometry.py\npython -m pytest -q test/test_extension_geometry.py > reports/extension-corpus/geometry-probes-2.txt", "exit_code": 1, "seconds": 2, "output_tail": ""}
{"timestamp": "2026-09-26T22:37:56.382Z", "command": "tail -n 65 reports/extension-corpus/geometry-probes-2.txt", "exit_code": 0, "seconds": 0, "output_tail": "ucted\n\nhearth/kernel/scene.py:391: ValidationError\n____________ test_polygon_hall_multiple_usable_ports[25-13-4-False] ____________\n\nwidth = 25, depth = 13, corner = 4, lantern = False\nwork = PosixPath('/work/generator/workspace/test/tmp-4esx4eff')\n\n    @pytest.mark.parametrize('width,depth,corner,lantern', [(13, 17, 0, False), (17, 21, 3, True), (25, 13, 4, False)])\n    def test_polygon_hall_multiple_usable_ports(width, depth, corner, lantern, work):\n        from hearth_extensions import Pavilion, Polygon\n        s = Scene(2, domain=Box((-15, -4, -15), (42, 60, 42)))\n        s.place('land', Terrain(s.domain, gradient(.025, -.018, 4)))\n>       s.place('hall', Pavilion(Polygon.chamfered(width, depth, corner), '/land', entrances=('north', 'east', 'south', 'west'), lantern=lantern))\n\ntest/test_extension_geometry.py:56: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nhearth/kernel/scene.py:149: in place\n    candidate._check(False)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <hearth.kernel.scene.Scene object at 0x7bb84b46e690>, final = False\ncompleted_scope = None\n\n    def _check(self, final, completed_scope=None):\n        errors = validate(self, final, completed_scope)\n        if errors:\n>           raise ValidationError(errors)\nE           hearth.kernel.errors.ValidationError: clearance: /hall/approach-north at (13, 7, -1): Protected player headroom is obstructed\nE           clearance: /hall/approach-south at (11, 7, 13): Protected player headroom is obstructed\n\nhearth/kernel/scene.py:391: ValidationError\n=========================== short test summary info ============================\nFAILED test/test_extension_geometry.py::test_polygon_hall_multiple_usable_ports[13-17-0-False]\nFAILED test/test_extension_geometry.py::test_polygon_hall_multiple_usable_ports[17-21-3-True]\nFAILED test/test_extension_geometry.py::test_polygon_hall_multiple_usable_ports[25-13-4-False]\n3 failed, 2 passed in 2.60s\n"}
{"timestamp": "2026-09-26T22:39:35.347Z", "command": "python - <<'PY'\nfrom pathlib import Path\np=Path('hearth_extensions/halls.py');s=p.read_text().replace('enumerate((-4, 2))','enumerate((-4, 3))');p.write_text(s)\nPY\ncat > hearth_extensions/graphs.py <<'PY'\n# Reusable campus graph composes arbitrary public building and circulation components.\nfrom dataclasses import dataclass\nfrom hearth import Box,Frame,ContractError\nfrom hearth.kernel import Capability,Contract,Port,Rule,Plan,Child,Context\nfrom .interfaces import Endpoint\nfrom .circulation import connection\nfrom .boundaries import Boundary,IlluminateBoundary\n\n\n@dataclass(frozen=True)\nclass Vertex:\n    key: str\n    component: object\n    frame: Frame\n\n\n@dataclass(frozen=True)\nclass Edge:\n    a: str\n    a_port: str\n    b: str\n    b_port: str\n    covered: bool = False\n    via: tuple = ()\n    rise: int = 0\n    width: int = 3\n\n\n@dataclass(frozen=True)\nclass Campus:\n    vertices: tuple[Vertex,...]\n    edges: tuple[Edge,...]\n    terrain: str\n    envelope: Box\n    boundary: object = None\n\n    def capability(self):\n        return Capability('extension.campus',('settlement','composite'),offers=('public-access',),guarantees=('declared graph connected','usable realized building connections'),locked=('vertices','edges'))\n\n    def negotiate(self,ctx,parameters):\n        keys={v.key for v in self.vertices}\n        if len(keys)!=len(self.vertices) or not keys:\n            raise ContractError('graph-vertices',ctx.path)\n        connected={self.vertices[0].key}\n        for edge in self.edges:\n            if edge.a not in keys or edge.b not in keys or edge.a==edge.b:\n                raise ContractError('graph-edge',ctx.path,conditions=str(edge))\n        while True:\n            more={v for e in self.edges if e.a in connected or e.b in connected for v in (e.a,e.b)}\n            if more<=connected:\n                break\n            connected|=more\n        if connected!=keys:\n            raise ContractError('graph-disconnected',ctx.path,conditions=str(sorted(keys-connected)))\n        first=self.vertices[0]\n        context=Context(ctx.path+'/'+first.key,ctx.frame.compose(first.frame),ctx.scope.child(first.key),ctx.view,(),ctx.limits)\n        offered=first.component.negotiate(context,{}).transformed(first.frame).ports\n        port=next((p for p in offered if p.kind in ('access','public-access')),None)\n        if port is None:\n            raise ContractError('graph-access',ctx.path,conditions='First vertex must expose usable access')\n        gateway=Port('public','access',port.frame,port.region,port.capacity,delegate=(first.key,port.key))\n        return Contract(self.envelope,ports=(gateway,),decisions={'vertices':[v.key for v in self.vertices],'edges':[(e.a,e.a_port,e.b,e.b_port,e.covered,e.width) for e in self.edges]})\n\n    def realize(self,ctx,contract):\n        p=Plan(children=[Child(v.key,v.component,frame=v.frame) for v in self.vertices])\n        p.children.append(Child('circulation',Connections(ctx.path,self.edges,self.terrain,self.envelope)))\n        if self.boundary is not None:\n            p.children.extend((Child('boundary',Boundary(self.boundary)),Child('boundary-lights',IlluminateBoundary(ctx.path+'/boundary',2))))\n        return p\n\n\n@dataclass(frozen=True)\nclass Connections:\n    parent: str\n    edges: tuple[Edge,...]\n    terrain: str\n    envelope: Box\n\n    def capability(self):\n        return Capability('extension.connection-graph',('connection','composite'))\n\n    def negotiate(self,ctx,parameters):\n        targets=[]\n        for edge in self.edges:\n            for key,port in ((edge.a,edge.a_port),(edge.b,edge.b_port)):\n                targets.append(ctx.local(ctx.view.port(self.parent+'/'+key,port).frame.origin))\n        return Contract(self.envelope,rules=(Rule('route',tuple(targets),self.envelope,phase='complete'),))\n\n    def realize(self,ctx,contract):\n        p=Plan()\n        for i,edge in enumerate(self.edges):\n            a=Endpoint(self.parent+'/'+edge.a,edge.a_port)\n            b=Endpoint(self.parent+'/'+edge.b,edge.b_port)\n            p.children.append(connection(f'link-{i}',a,b,self.terrain,via=edge.via,covered=edge.covered,rise=edge.rise,width=edge.width))\n        return p\nPY\ncat > hearth_extensions/programs.py <<'PY'\n# Lightweight graph clients using frozen generic fields and interchangeable public buildings.\nfrom hearth import Scene,Box,Frame\nfrom hearth.randomness import Scope\nfrom hearth.environment import Terrain,gradient,wave,radial\nfrom hearth.components.building import Blueprint,RoomSpec,Building\nfrom hearth.persistence import semantic_digest\nfrom .geometry import Polygon,Polyline\nfrom .halls import Pavilion\nfrom .graphs import Campus,Vertex,Edge\n\nDOMAIN=Box((-18,-4,-18),(95,70,95))\n\n\ndef frozen_site(environment_seed=0,domain=DOMAIN):\n    rng=Scope(environment_seed).stream('environment')\n    field=gradient(rng.choice((-.012,0,.012)),rng.choice((-.009,.009)),5)+wave(.5,.07,.09,rng.random()*6)+radial(30,31,24,-3.8)\n    scene=Scene(environment_seed,domain=domain)\n    scene.place('land',Terrain(domain,field,water_level=3,edit_limit=2500))\n    return scene.finalize()\n\n\ndef branching_campus(seed=0):\n    rng=Scope(seed).stream('branching-graph')\n    hub=Pavilion(Polygon.chamfered(17,17,4),'/land','library',('north','east','south','west'),height=6,lantern=True)\n    west=Pavilion(Polygon.chamfered(13,rng.choice((13,17)),2),'/land','workshop',('north','south'),height=5)\n    east=Pavilion(Polygon.chamfered(rng.choice((13,17)),13,2),'/land','kitchen',('west','south'),height=rng.choice((5,6)))\n    rooms=[RoomSpec(0,0,0,'living'),RoomSpec(0,0,1,'bedroom')]\n    if rng.choice((False,True)):\n        rooms.append(RoomSpec(0,1,0,'storage'))\n    lodge=Building(Blueprint(tuple(rooms),bay=9,porch=True),'/land')\n    vertices=[Vertex('hall',hub,Frame((23,0,23))),Vertex('workshop',west,Frame((0,0,0))),Vertex('kitchen',east,Frame((51,0,0))),Vertex('lodge',lodge,Frame((48,0,51)))]\n    edges=[Edge('hall','north','workshop','south',covered=True),Edge('hall','east','kitchen','west',rise=rng.choice((0,1))),Edge('hall','south','lodge','street',covered=True)]\n    if rng.choice((False,True)):\n        vertices.append(Vertex('archive',Pavilion(Polygon.chamfered(13,13,2),'/land','library',('east',)),Frame((0,0,51))))\n        edges.append(Edge('hall','west','archive','east',rise=1))\n    boundary=Polyline.bezier(((-9,10),(-12,90),(85,95),(84,5)))\n    return Campus(tuple(vertices),tuple(edges),'/land',DOMAIN,boundary)\n\n\ndef perimeter_cluster(seed=0):\n    rng=Scope(seed).stream('perimeter-graph')\n    widths=(rng.choice((17,21,25)),rng.choice((13,17)),rng.choice((13,17)))\n    market=Pavilion(Polygon.chamfered(widths[0],13,2),'/land','workshop',('north','east','south'),height=5,roof_run=3)\n    reading=Pavilion(Polygon.chamfered(widths[1],17,3),'/land','library',('north','south'),height=6,lantern=rng.choice((True,False)))\n    dining=Pavilion(Polygon.chamfered(widths[2],13,2),'/land','living',('east','west'),height=5)\n    vertices=(Vertex('market',market,Frame((0,0,0))),Vertex('reading',reading,Frame((50,0,10))),Vertex('dining',dining,Frame((20,0,57))))\n    edges=(Edge('market','east','reading','north',via=((40,-5),),covered=False,rise=1),Edge('reading','south','dining','east',covered=True),Edge('dining','west','market','south',via=((9,42),),rise=rng.choice((0,1))))\n    boundary=Polyline.bezier(((-6,25),(-2,86),(83,88),(83,20)))\n    return Campus(vertices,edges,'/land',DOMAIN,boundary)\n\n\ndef extension_scene(program=branching_campus,seed=0,environment_seed=0):\n    environment=frozen_site(environment_seed)\n    original=semantic_digest(environment)\n    scene=environment.fork(seed=seed)\n    scene.place('campus',program(seed))\n    scene.finalize()\n    if semantic_digest(environment)!=original:\n        raise AssertionError('Frozen input mutated')\n    return scene\nPY\nyapf -i hearth_extensions/halls.py hearth_extensions/graphs.py hearth_extensions/programs.py\npython -m pytest -q test/test_extension_geometry.py > reports/extension-corpus/geometry-probes-3.txt\npython - <<'PY' > reports/extension-corpus/client-probes.txt\nfrom hearth_extensions.programs import *\nfor program in (branching_campus,perimeter_cluster):\n for seed in (0,1):\n  try:\n   s=extension_scene(program,seed,seed)\n   print(program.__name__,seed,'valid',len(s.blocks),flush=True)\n  except Exception as e:\n   print(program.__name__,seed,type(e).__name__,str(e),flush=True)\nPY", "exit_code": 0, "seconds": 13, "output_tail": ""}
{"timestamp": "2026-09-26T22:40:27.714Z", "command": "cat reports/extension-corpus/geometry-probes-3.txt\ncat reports/extension-corpus/client-probes.txt", "exit_code": 0, "seconds": 0, "output_tail": ".....                                                                    [100%]\n5 passed in 5.46s\nbranching_campus 0 ContractError boundary-domain: /campus/boundary at None: Simple open contour, at most 220 raster cells\nbranching_campus 1 ContractError boundary-domain: /campus/boundary at None: Simple open contour, at most 220 raster cells\nperimeter_cluster 0 ContractError boundary-grade: /campus/boundary at None: Adjacent wall foundations exceed step bound\nperimeter_cluster 1 ContractError boundary-grade: /campus/boundary at None: Adjacent wall foundations exceed step bound\n"}
{"timestamp": "2026-09-26T22:41:04.630Z", "command": "python - <<'PY'\nfrom dataclasses import replace\nfrom collections import Counter\nfrom hearth_extensions.programs import *\nfor program in (branching_campus,perimeter_cluster):\n c=program(0);pts=c.boundary.cells()\n print(program.__name__,'curve',len(pts),len(set(pts)),'repeat',[(p,n) for p,n in Counter(pts).items() if n>1][:8])\n s=frozen_site(0).fork(seed=0);s.place('campus',replace(c,boundary=None))\n rows=[(p,s.view.elevation(*p)) for p in pts]\n for (p,a),(q,b) in zip(rows,rows[1:]):\n  if abs(a-b)>1:\n   print('grade',p,a,q,b,s.inspect((p[0],a,p[1]))['instance'],s.inspect((q[0],b,q[1]))['instance'])\nPY", "exit_code": 1, "seconds": 1, "output_tail": "branching_campus curve 221 221 repeat []\nTraceback (most recent call last):\n  File \"<stdin>\", line 7, in <module>\n  File \"/work/generator/workspace/hearth/kernel/scene.py\", line 149, in place\n    candidate._check(False)\n  File \"/work/generator/workspace/hearth/kernel/scene.py\", line 391, in _check\n    raise ValidationError(errors)\nhearth.kernel.errors.ValidationError: clearance: /campus/circulation/link-0 at (29, 6, 22): Protected player headroom is obstructed\nclearance: /campus/circulation/link-0 at (33, 6, 22): Protected player headroom is obstructed\nclearance: /campus/circulation/link-1 at (40, 6, 29): Protected player headroom is obstructed\nclearance: /campus/circulation/link-1 at (40, 6, 33): Protected player headroom is obstructed\nclearance: /campus/circulation/link-2 at (29, 6, 40): Protected player headroom is obstructed\nclearance: /campus/circulation/link-2 at (33, 6, 40): Protected player headroom is obstructed\n"}
{"timestamp": "2026-09-26T22:41:38.694Z", "command": "sed -n '457,495p' hearth/kernel/scene.py", "exit_code": 0, "seconds": 0, "output_tail": "        self._commit(trial)\n\n    def remove(self, path):\n        trial = self._fork()\n        trial._remove_branch(path)\n        trial._check(False)\n        self._commit(trial)\n\n    def _remove_branch(self, path):\n        paths = {k for k in self.nodes if k == path or k.startswith(path + '/')}\n        affected = set()\n        for k in sorted(paths, reverse=True):\n            current = set(self.reverse[k]) | set(self.displaced.get(k, {}))\n            for p in current:\n                record = self.blocks.get(p)\n                if record is None and self.erasures.get(p) not in paths:\n                    continue\n                if record is not None and record.owner not in paths and self.operations[record.operation]['component'] not in paths:\n                    continue\n                affected.add(p)\n                if record is not None:\n                    self.reverse[record.owner].discard(p)\n                self.erasures.pop(p, None)\n                previous = self.displaced.get(k, {}).get(p)\n                if previous and previous.owner in self.nodes and previous.owner not in paths:\n                    self.blocks[p] = previous\n                    self.reverse[previous.owner].add(p)\n                else:\n                    self.blocks.pop(p, None)\n            self.displaced.pop(k, None)\n        for d in self.dependencies:\n            if d['consumer'] not in paths and (d['source'] in paths or any(d['region'].contains(p) for p in affected)):\n                self.nodes[d['consumer']].stale = True\n        for k in sorted(paths, reverse=True):\n            self.reverse_shared.pop(k, None)\n            self.reverse[k].clear()\n            self._retire(k)\n        self.relations = {r for r in self.relations if r[0] in self.nodes and r[2] in self.nodes}\n        for p, record in list(self.blocks.items()):\n"}
{"timestamp": "2026-09-26T22:43:16.801Z", "command": "cat >> hearth_extensions/boundaries.py <<'PY'\n\n\n@dataclass(frozen=True)\nclass BoundaryChain:\n    curve: Polyline\n    height: int = 2\n    spacing: int = 7\n    chunk: int = 96\n\n    def capability(self):\n        return Capability('extension.boundary-chain',('boundary','composite'),offers=('mount',),guarantees=('continuous contour across component joins',))\n\n    def parts(self):\n        cells=self.curve.cells()\n        if len(set(cells))!=len(cells):\n            raise ContractError('boundary-self-intersection',conditions='A simple open contour is required')\n        count=max(1,(len(cells)+self.chunk-1)//self.chunk)\n        size=(len(cells)+count-1)//count\n        return tuple(Boundary(Polyline(cells[i:i+size]),self.height,self.spacing) for i in range(0,len(cells),size))\n\n    def negotiate(self,ctx,parameters):\n        boxes=[];ports=[]\n        for i,part in enumerate(self.parts()):\n            local=part.negotiate(ctx,{})\n            boxes.append(local.envelope)\n            for p in local.ports:\n                ports.append(Port(f'segment-{i}-{p.key}',p.kind,p.frame,p.region,p.capacity,facts=p.facts,delegate=(f'segment-{i}',p.key)))\n        return Contract(Box.enclosing(p for box in boxes for p in box.corners()),ports=tuple(ports),decisions={'segments':len(boxes),'curve':self.curve.points})\n\n    def realize(self,ctx,contract):\n        return Plan(children=[Child(f'segment-{i}',part) for i,part in enumerate(self.parts())])\nPY\npython - <<'PY'\nfrom pathlib import Path\np=Path('hearth_extensions/graphs.py');s=p.read_text().replace('from .boundaries import Boundary, IlluminateBoundary','from .boundaries import BoundaryChain, IlluminateBoundary').replace('Boundary(self.boundary)','BoundaryChain(self.boundary)');p.write_text(s)\np=Path('hearth_extensions/programs.py');s=p.read_text().replace('((-9, 10), (-12, 90), (85, 95), (84, 5))','((-12, 0), (-30, 120), (115, 120), (84, 0))').replace('((-6, 25), (-2, 86), (83, 88), (83, 20))','((-12, 0), (-30, 120), (115, 120), (84, 0))');p.write_text(s)\nPY\nyapf -i hearth_extensions/circulation.py hearth_extensions/boundaries.py hearth_extensions/graphs.py hearth_extensions/programs.py\npython - <<'PY' > reports/extension-corpus/client-probes-2.txt\nfrom hearth_extensions.programs import *\nfor program in (branching_campus,perimeter_cluster):\n for seed in (0,1):\n  try:\n   s=extension_scene(program,seed,seed)\n   print(program.__name__,seed,'valid',len(s.blocks),flush=True)\n  except Exception as e:\n   print(program.__name__,seed,type(e).__name__,str(e),flush=True)\nPY", "exit_code": 0, "seconds": 11, "output_tail": ""}
{"timestamp": "2026-09-26T22:44:35.566Z", "command": "cat > test/test_extension_failures.py <<'PY'\n# Deliberate input and geometry damage for extension contracts and atomicity.\nfrom dataclasses import replace\nimport copy\nimport pytest\nfrom hearth import Scene,Box,Frame,Binding,ContractError\nfrom hearth.kernel import validate,Child,Plan,Choice,Limits\nfrom hearth.blocks import state_of\nfrom hearth.components.primitives import Volume\nfrom hearth.environment import Terrain,gradient,wave\nfrom hearth.persistence import semantic_digest,export_scene,load_scene,encode\nfrom hearth_extensions import Polyline,Endpoint,Link,Boundary,IlluminateBoundary,Platform\nfrom hearth_extensions.boundaries import BoundaryChain\nfrom hearth_extensions.circulation import Trestles\nfrom test_extension_geometry import bridge_scene\n\n\ndef snapshot(s):\n    return (semantic_digest(s),copy.deepcopy(encode(s.nodes)),copy.deepcopy(encode(s.relations)),copy.deepcopy(encode(s.dependencies)),copy.deepcopy(encode(s.operations)),copy.deepcopy(s.reverse),copy.deepcopy(s.port_uses),copy.deepcopy(s.events))\n\n\ndef damage(s,p):\n    record=s.blocks.pop(p)\n    s.reverse[record.owner].remove(p)\n\n\n@pytest.mark.parametrize('kind',['support','guard','endpoint','headroom'])\ndef test_broken_bridge_geometry_is_diagnosed(kind):\n    s=bridge_scene()\n    if kind=='support':\n        p=s.nodes['/link/supports'].contract.rules[0].cells[0]\n        damage(s,p)\n        expected='support'\n    elif kind=='guard':\n        p=next(iter(s.cells('/link/guards')))\n        damage(s,p)\n        expected='bridge-guard'\n    elif kind=='endpoint':\n        a=s.view.port('/a','south').frame.origin\n        p=(a[0],a[1]-1,a[2]);damage(s,p)\n        expected='reachability'\n    else:\n        p=next(r for r in s.nodes['/link'].contract.rules if r.kind=='clear').cells[len(s.nodes['/link'].contract.decisions['deck'])//2]\n        before=snapshot(s)\n        with pytest.raises(ContractError,match='clearance'):\n            s.place('blocked',Volume(Box(p,p),state_of('stone')))\n        assert snapshot(s)==before\n        return\n    errors=validate(s,True)\n    assert any(d.rule==expected and d.position is not None for d in errors)\n\n\n@pytest.mark.parametrize('fault',['endpoint','frame','kind'])\ndef test_invalid_connections_rollback_every_index(fault):\n    s=bridge_scene();s.remove('/link')\n    a,b=Endpoint('/a','south'),Endpoint('/b','north')\n    pa=s.view.port('/a','south').frame.origin\n    pb=s.view.port('/b','north').frame.origin\n    curve=Polyline(((pa[0],pa[2]),(pa[0]+3,pa[2]),(pb[0],pb[2])))\n    expected='endpoint-frame'\n    if fault=='endpoint':\n        curve=Polyline(((pa[0]+1,pa[2]),(pb[0],pb[2])))\n        expected='endpoint-disconnected'\n    if fault=='kind':\n        a=Endpoint('/land','construction')\n        expected='endpoint-kind'\n    before=snapshot(s)\n    with pytest.raises(ContractError,match=expected):\n        s.place('bad',Link(a,b,'/land',curve=curve),bindings=(a.binding(),b.binding()))\n    assert snapshot(s)==before\n\n\ndef test_connection_permission_overflow_is_atomic():\n    s=Scene(0,domain=Box((-3,-4,-3),(12,20,12)))\n    s.place('land',Terrain(s.domain,gradient(base=0),water_level=5,edit_limit=1))\n    before=snapshot(s)\n    columns=((2,0,2,4,True),(3,0,2,4,True))\n    with pytest.raises(ContractError,match='edit-limit'):\n        s.place('piers',Trestles(columns),bindings=(Binding('/land','construction','fill'),))\n    assert snapshot(s)==before\n\n\ndef test_connection_remove_and_regenerate_restore_water_and_membership():\n    s=bridge_scene();s.remove('/link')\n    before=snapshot(s)\n    a,b=Endpoint('/a','south'),Endpoint('/b','north')\n    comp=Link(a,b,'/land',rise=1)\n    s.place('link',comp,bindings=(a.binding(),b.binding()))\n    old=s.cells('/link',True)\n    s.regenerate('/link',replace(comp,covered=True,rise=0))\n    new=s.cells('/link',True)\n    assert old!=new\n    assert all(s.inspect(p)['instance'] in s.nodes for p in new)\n    assert all(not s.inspect(p)['instance'] or not s.inspect(p)['instance'].startswith('/link') for p in old-new)\n    s.remove('/link')\n    assert semantic_digest(s)==before[0]\n    assert not any('/link' in a or '/link' in c for a,b,c in s.relations)\n    assert not any(k.startswith('/link') for k in s.nodes)\n\n\ndef test_boundary_missing_coping_support_and_whole_removal(work):\n    s=Scene(0,domain=Box((-5,-4,-5),(45,24,25)))\n    s.place('land',Terrain(s.domain,gradient(base=3)))\n    curve=Polyline.bezier(((0,0),(9,20),(26,0),(37,17)))\n    s.place('boundary',BoundaryChain(curve))\n    s.place('lamps',IlluminateBoundary('/boundary'))\n    before=snapshot(s)\n    with pytest.raises(ContractError):\n        s.remove('/boundary')\n    assert snapshot(s)==before\n    changed=Polyline.bezier(((0,1),(9,19),(26,1),(37,18)))\n    s.regenerate('/boundary',BoundaryChain(changed),dependents=('/lamps',))\n    s.finalize()\n    p=s.view.offers('/boundary','mount')[0].frame.origin\n    assert p in s.cells('/boundary',True)\n    export_scene(s,work/'curve.litematic')\n    t=load_scene(work/'curve.litematic')\n    assert t.inspect(p)==s.inspect(p)\n    base=(p[0],p[1]-1,p[2]);damage(t,base)\n    assert any(d.rule in ('support','enclosure') and d.position==base for d in validate(t,True))\n\n\ndef test_curve_boundary_rejects_incompatible_installation_frame():\n    from hearth_extensions import BoundaryLantern\n    s=Scene(0,domain=Box((-3,-4,-3),(25,25,25)))\n    s.place('land',Terrain(s.domain,gradient(base=3)))\n    s.place('wall',Boundary(Polyline(((0,0),(20,8)))))\n    port=s.view.offers('/wall','mount')[0]\n    before=snapshot(s)\n    with pytest.raises(ContractError,match='port-alignment'):\n        s.place('bad-lamp',BoundaryLantern(),frame=Frame(port.frame.origin,port.frame.turn+1),bindings=(Binding('/wall',port.key),))\n    assert snapshot(s)==before\n\n\ndef test_failed_choice_does_not_perturb_sibling_randomness():\n    s=bridge_scene();s.remove('/link')\n    a,b=Endpoint('/a','south'),Endpoint('/b','north')\n    before=snapshot(s)\n    bad=Link(a,b,'/land',fill_limit=0)\n    with pytest.raises(ContractError):\n        s.compose_choice(Choice('link',(Child('attempt',bad,bindings=(a.binding(),b.binding())),),lambda v,p:0))\n    assert snapshot(s)==before\n    good=Link(a,b,'/land',rise=1)\n    s.place('link',good,bindings=(a.binding(),b.binding()))\n    assert semantic_digest(s)==semantic_digest(bridge_scene())\nPY\npython -m pytest -q test/test_extension_protocol.py test/test_extension_geometry.py test/test_extension_failures.py > reports/extension-corpus/focused-tests.txt", "exit_code": 1, "seconds": 10, "output_tail": ""}
{"timestamp": "2026-09-26T22:44:37.896Z", "command": "cat reports/extension-corpus/client-probes-2.txt\ncat reports/extension-corpus/focused-tests.txt", "exit_code": 0, "seconds": 0, "output_tail": "achability: /campus/circulation/link-0 at (6, 5, 22): No walkable route with two blocks of headroom from entrance\nperimeter_cluster 0 valid 116962\nperimeter_cluster 1 valid 127803\n...............F...                                                      [100%]\n=================================== FAILURES ===================================\n______ test_connection_remove_and_regenerate_restore_water_and_membership ______\n\n    def test_connection_remove_and_regenerate_restore_water_and_membership():\n        s=bridge_scene();s.remove('/link')\n        before=snapshot(s)\n        a,b=Endpoint('/a','south'),Endpoint('/b','north')\n        comp=Link(a,b,'/land',rise=1)\n        s.place('link',comp,bindings=(a.binding(),b.binding()))\n        old=s.cells('/link',True)\n>       s.regenerate('/link',replace(comp,covered=True,rise=0))\n\ntest/test_extension_failures.py:89: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nhearth/kernel/scene.py:518: in regenerate\n    trial._check(True)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <hearth.kernel.scene.Scene object at 0x7a295aad3830>, final = True\ncompleted_scope = None\n\n    def _check(self, final, completed_scope=None):\n        errors = validate(self, final, completed_scope)\n        if errors:\n>           raise ValidationError(errors)\nE           hearth.kernel.errors.ValidationError: unfinished: /a at None: Scope unfinished or dependent inputs stale\nE           unfinished: /a/foundation at None: Scope unfinished or dependent inputs stale\nE           unfinished: /b at None: Scope unfinished or dependent inputs stale\nE           unfinished: /b/foundation at None: Scope unfinished or dependent inputs stale\n\nhearth/kernel/scene.py:391: ValidationError\n=========================== short test summary info ============================\nFAILED test/test_extension_failures.py::test_connection_remove_and_regenerate_restore_water_and_membership\n1 failed, 18 passed in 10.32s\n"}
{"timestamp": "2026-09-26T22:46:24.494Z", "command": "cat >> test/test_extension_protocol.py <<'PY'\n\n\ndef test_atomic_multi_branch_replay_accepts_explicit_replacements():\n    from hearth.components.primitives import Volume\n    s=Scene()\n    s.place('a',Volume(Box((0,0,0),(0,0,0)),state_of('stone')))\n    s.place('b',Volume(Box((3,0,0),(3,0,0)),state_of('stone')))\n    s.regenerate('/a',dependents=('/b',),replacements={'/b':Volume(Box((3,0,0),(4,0,0)),state_of('oak_planks'))})\n    assert len(s.cells('/b'))==2\n    assert s.inspect((0,0,0))['state']==state_of('stone')\nPY\npython -m pytest -q test/test_extension_protocol.py::test_atomic_multi_branch_replay_accepts_explicit_replacements > reports/extensions-baseline/failing-replay-probe.txt", "exit_code": 1, "seconds": 0, "output_tail": ""}
{"timestamp": "2026-09-26T22:46:47.472Z", "command": "python - <<'PY'\nfrom pathlib import Path\np=Path('test/test_extension_failures.py');s=p.read_text().replace(\"s.regenerate('/link',replace(comp,covered=True,rise=0))\", \"s.regenerate('/a', dependents=('/b','/link'), replacements={'/link':replace(comp,covered=True,rise=0)})\");p.write_text(s)\nPY\ncat >> reports/extensions-baseline/api-delta.md <<'MD'\n\n## Explicit replacements in a coupled replay\n\nThe bridge replacement test exposed conservative host invalidation: removing contact output can\ninvalidate earlier platform surveys even though their principal intent is unchanged. Replaying only\nthe link correctly refuses to finalize stale platform observations. Replaying hosts and link together\nwas possible, but the public API could only replace the first target, while the changed link must\ncome after its hosts. The proposed `replacements` call failed before the change; its TypeError is\nretained in `failing-replay-probe.txt`, and the original stale-input result remains in\n`../extension-corpus/focused-tests.txt`.\n\n`Scene.regenerate(..., dependents=(...), replacements={path: component, ...})` now accepts explicit\nreplacements for any named replay target. Targets remain explicitly ordered, retain their frames,\nscopes and bindings, and replay in one bounded transaction. Undeclared or duplicate targets fail.\nNo automatic graph-wide update loop or stale-flag bypass was added. A link can be replaced while its\nunchanged hosts are recomputed first from their inputs. Reloaded branches may likewise supply their\nimplementations explicitly through this mapping. Existing calls keep their previous behavior.\nMD\nyapf -i hearth/kernel/scene.py hearth_extensions/geometry.py test/test_extension_protocol.py test/test_extension_failures.py\npython -m pytest -q test/test_extension_protocol.py test/test_extension_geometry.py test/test_extension_failures.py test/test_kernel.py test/test_contracts_provenance.py > reports/extension-corpus/focused-tests-2.txt\npython - <<'PY' > reports/extension-corpus/client-probes-3.txt\nfrom hearth_extensions.programs import *\nfor program in (branching_campus,perimeter_cluster):\n for seed in (0,1):\n  try:\n   s=extension_scene(program,seed,seed)\n   print(program.__name__,seed,'valid',len(s.blocks),flush=True)\n  except Exception as e:\n   print(program.__name__,seed,type(e).__name__,str(e),flush=True)\nPY", "exit_code": 0, "seconds": 22, "output_tail": ""}
{"timestamp": "2026-09-26T22:47:53.198Z", "command": "cat reports/extension-corpus/focused-tests-2.txt\ncat reports/extension-corpus/client-probes-3.txt\ncat reports/extension-corpus/campus-development.txt", "exit_code": 0, "seconds": 0, "output_tail": ".............................................                            [100%]\n45 passed in 10.60s\nbranching_campus 0 valid 117534\nbranching_campus 1 valid 128715\nperimeter_cluster 0 valid 116962\nperimeter_cluster 1 valid 127803\nsamples/extensions/campus-development.litematic 73d0687f6604f727248d0f0cafc4d6a2f7885c77d5aae638ca6e117e9e4ebea5 117534 blocks 164 instances\n"}
{"timestamp": "2026-09-26T22:48:02.197Z", "command": "cat > examples/extension_generate.py <<'PY'\n# Explicit client selection for new graph compositions, preserving the original default CLI.\nimport argparse\nfrom pathlib import Path\nfrom hearth.persistence import export_scene,semantic_digest\nfrom hearth_extensions.programs import extension_scene,branching_campus,perimeter_cluster\n\nPROGRAMS={'campus':branching_campus,'perimeter':perimeter_cluster}\n\n\ndef main():\n    parser=argparse.ArgumentParser()\n    parser.add_argument('program',choices=PROGRAMS)\n    parser.add_argument('--seed',type=int,default=0)\n    parser.add_argument('--environment-seed',type=int,default=0)\n    parser.add_argument('--output',type=Path,default=Path('samples/extensions/composition.litematic'))\n    args=parser.parse_args()\n    scene=extension_scene(PROGRAMS[args.program],args.seed,args.environment_seed)\n    export_scene(scene,args.output)\n    print(args.output,semantic_digest(scene),len(scene.blocks),'blocks',len(scene.nodes),'instances')\n\n\nif __name__=='__main__':\n    main()\nPY\ncat > examples/extension_views.py <<'PY'\n# Render extension artifact pairs and explicitly labeled geometry-only inspection sections.\nimport argparse\nimport json\nfrom pathlib import Path\nimport subprocess\nimport sys\nimport hearth_extensions\nfrom hearth.persistence import load_scene,companion_path\nfrom examples.preview import render,CUTAWAY\n\n\ndef views(seed,source,output,size=1100,sections=False):\n    scene=load_scene(source)\n    if scene.seed!=seed:\n        raise ValueError('Explicit seed must match artifact')\n    output.mkdir(parents=True,exist_ok=True)\n    records=[]\n    for label,azimuth,elevation in (('front',215,36),('rear',35,32),('side',120,27)):\n        target=output/(label+'.png')\n        render(source,target,azimuth,elevation,size,3.6)\n        records.append({'source':str(source),'seed':seed,'label':label,'azimuth':azimuth,'elevation':elevation,'image':str(target)})\n    if sections:\n        halls=[n for n in scene.nodes.values() if n.type=='extension.pavilion']\n        links=[n for n in scene.nodes.values() if n.type in ('extension.bridge','extension.gallery')]\n        height=max(n.frame.origin[1]+n.contract.decisions['floor']+3 for n in halls) if halls else max(p[1] for n in links for p in scene.cells(n.path+'/deck'))+1\n        cuts=[('floor-cut','y',height,215,65)]\n        if links:\n            points=scene.cells(links[0].path+'/deck')\n            cuts.append(('connection-section','x',(min(p[0] for p in points)+max(p[0] for p in points))//2,110,20))\n        offset=json.loads(companion_path(source).read_text())['offset']\n        for label,axis,world,azimuth,elevation in cuts:\n            target=output/(label+'.litematic')\n            limit=world-offset['xyz'.index(axis)]\n            subprocess.run([sys.executable,str(CUTAWAY),'--input',str(source),'--output',str(target),'--axis',axis,'--max',str(limit),'--name',label],check=True)\n            image=output/(label+'.png')\n            render(target,image,azimuth,elevation,size,3.6)\n            records.append({'source':str(source),'seed':seed,'label':label,'axis':axis,'world_cut':world,'export_local_cut':limit,'geometry_preview_only':True,'image':str(image)})\n    (output/'views.json').write_text(json.dumps(records,indent=2))\n\n\nif __name__=='__main__':\n    p=argparse.ArgumentParser()\n    p.add_argument('--seed',type=int,default=0)\n    p.add_argument('--input',type=Path,required=True)\n    p.add_argument('--output',type=Path,required=True)\n    p.add_argument('--size',type=int,default=1100)\n    p.add_argument('--sections',action='store_true')\n    a=p.parse_args()\n    views(a.seed,a.input,a.output,a.size,a.sections)\nPY\nyapf -i examples/extension_generate.py examples/extension_views.py\npython -m examples.extension_generate campus --seed 0 --environment-seed 0 --output samples/extensions/campus-development.litematic > reports/extension-corpus/campus-development.txt\npython -m examples.extension_views --seed 0 --input samples/extensions/campus-development.litematic --output previews/extensions/development --size 1000 --sections > reports/extension-corpus/development-render.txt", "exit_code": 0, "seconds": 25, "output_tail": ""}
{"timestamp": "2026-09-26T22:49:04.388Z", "command": "cat >> test/test_extension_geometry.py <<'PY'\n\n\n@pytest.mark.parametrize('turn',[0,1,2,3])\ndef test_extension_transforms_geometry_interfaces_claims_and_queries(turn,work):\n    from hearth.blocks import blockstates_equivalent\n    frame=Frame((7,3,-5),turn)\n    base=bridge_scene()\n    moved=bridge_scene(frame=frame)\n    assert len(base.blocks)==len(moved.blocks)\n    for point,cell in base.blocks.items():\n        q=frame.point(point)\n        assert blockstates_equivalent(frame.state(cell.state),moved.inspect(q)['state'])\n        assert moved.inspect(q)['instance']==cell.owner\n    assert moved.view.port('/a','south').frame==frame.compose(base.view.port('/a','south').frame)\n    assert moved.nodes['/link'].contract.rules[0].cells==tuple(frame.point(p) for p in base.nodes['/link'].contract.rules[0].cells)\n    export_scene(moved,work/'rotated bridge.litematic')\n    restored=load_scene(work/'rotated bridge.litematic')\n    q=next(iter(moved.cells('/link/deck')))\n    assert restored.inspect(q)==moved.inspect(q)\n\n\ndef test_untried_kinked_ribbon_and_asymmetric_polygon():\n    from hearth_extensions import Polygon,Pavilion\n    # Endpoints share Z; a global endpoint clipping plane would sever this route.\n    curve=Polyline(((4,8),(4,12),(13,16),(22,12),(22,8)))\n    s=Scene(19,domain=Box((-8,-4,-8),(50,60,50)))\n    s.place('land',Terrain(s.domain,gradient(base=2),water_level=3))\n    s.place('a',Platform('/land'))\n    s.place('b',Platform('/land'),frame=Frame((18,0,0)))\n    a,b=Endpoint('/a','south'),Endpoint('/b','south')\n    s.place('link',Link(a,b,'/land',curve=curve,covered=True),bindings=(a.binding(),b.binding()))\n    s.finalize()\n    # A six-sided asymmetric footprint absent from the shipped clients.\n    polygon=Polygon(((2,0),(18,0),(22,4),(22,16),(0,16),(0,3)))\n    other=Scene(19,domain=Box((-10,-4,-10),(40,60,40)))\n    other.place('land',Terrain(other.domain,gradient(.01,.02,3)))\n    other.place('hall',Pavilion(polygon,'/land',entrances=('north','south'),height=7,roof_run=3))\n    other.finalize()\nPY\ncat >> test/test_extension_protocol.py <<'PY'\n\n\ndef test_multi_branch_replay_rejects_undeclared_target_and_rolls_back():\n    from hearth.components.primitives import Volume\n    from hearth.kernel import ContractError\n    from hearth.persistence import semantic_digest\n    s=Scene()\n    s.place('a',Volume(Box((0,0,0),(0,0,0)),state_of('stone')))\n    before=semantic_digest(s)\n    with pytest.raises(ContractError,match='replay-targets'):\n        s.regenerate('/a',replacements={'/unknown':Landing()})\n    assert semantic_digest(s)==before and set(s.nodes)=={'/a'}\nPY\nyapf -i test/test_extension_geometry.py test/test_extension_protocol.py\npython -m pytest -q test/test_extension_geometry.py test/test_extension_protocol.py > reports/extension-corpus/transforms-and-novelty.txt", "exit_code": 0, "seconds": 12, "output_tail": ""}
{"timestamp": "2026-09-26T22:49:48.085Z", "command": "tail -n 8 reports/extension-corpus/transforms-and-novelty.txt", "exit_code": 0, "seconds": 0, "output_tail": "..............                                                           [100%]\n14 passed in 11.88s\n"}
{"timestamp": "2026-09-26T22:50:18.826Z", "command": "sed -n '106,180p' hearth/components/landscape.py", "exit_code": 0, "seconds": 0, "output_tail": ":\n                return False\n            h = ctx.elevation(x, z)\n            if name_of(ctx.state((x, h, z))) != 'grass_block':\n                return False\n            box = Box((x - 3, h + 1, z - 3), (x + 3, h + 16, z + 3))\n            return all(ctx.view.domain.contains(ctx.world(c)) for c in box.corners()) and ctx.view.clear(ctx.frame.box(box)) and not ctx.view.protected(ctx.frame.box(Box((x - 3, h, z - 3), (x + 3, h + 2, z + 3))))\n\n        chosen = scatter(ctx, candidates, legal, self.tree_minimum, self.tree_maximum, 'trees')\n        for i, (x, z) in enumerate(chosen):\n            y = ctx.elevation(x, z) + 1\n            h = ctx.scope.child(f'tree-{i}').choose((9, 11, 13, 15), 'height')\n            p.children.append(Child(f'tree-{i}', Tree(h), frame=Frame((x, y, z)), bindings=(Binding(self.terrain, 'planting', 'plant'),)))\n        rng = ctx.rng('groundcover')\n        for x in range(x0 + 1, x1):\n            for z in range(z0 + 1, z1):\n                if rng.random() > self.density or any(abs(x - a) <= 3 and abs(z - b) <= 3 for a, b in chosen):\n                    continue\n                h = ctx.elevation(x, z)\n                if name_of(ctx.state((x, h, z))) != 'grass_block':\n                    continue\n                if name_of(ctx.state((x, h + 1, z))) != 'air':\n                    continue\n                if ctx.view.protected(ctx.frame.box(Box((x, h + 1, z), (x, h + 2, z)))):\n                    continue\n                p.children.append(Child(f'grass-{x}-{z}', Grass(), frame=Frame((x, h + 1, z)), bindings=(Binding(self.terrain, 'planting', 'plant'),)))\n        return p\n\n\n@dataclass(frozen=True)\nclass FacadeGarden:\n    width: int\n    depth: int\n    sides: tuple[str, ...]\n\n    def capability(self):\n        return Capability('facade.garden', ('decoration', 'plant'), adaptations=('avoid facade openings',), guarantees=('unobstructed doorways',))\n\n    def negotiate(self, ctx, parameters):\n        return Contract(Box((-1, 0, -1), (self.width, 3, self.depth)))\n"}
{"timestamp": "2026-09-26T22:54:14.202Z", "command": "tail -n 16 reports/extension-corpus/first-assessment.log\nrg -n 'incompatible|defect|budget' reports/extension-corpus/first-assessment.log", "exit_code": 1, "seconds": 0, "output_tail": "hall-4 validated\nhall-5 validated\nhall-6 validated\nhall-7 validated\nhall-8 validated\nhall-9 validated\nhall-10 validated\nhall-11 validated\ncampus-0 validated\ncampus-1 validated\ncampus-2 validated\ncampus-3 validated\ncampus-4 validated\ncampus-5 validated\nperimeter-0 validated\nperimeter-1 validated\n"}
{"timestamp": "2026-09-26T22:54:44.064Z", "command": "cat > hearth_extensions/assessment.py <<'PY'\n# Frozen extension inputs, actual principal/contact signatures and retained outcomes.\nimport hashlib\nimport json\nimport time\nfrom pathlib import Path\nfrom dataclasses import replace\nfrom collections import Counter\nfrom hearth import Scene,Box,Frame,Binding\nfrom hearth.kernel import ContractError,SearchExhausted,validate\nfrom hearth.environment import Terrain,gradient,wave,radial\nfrom hearth.randomness import Scope\nfrom hearth.persistence import semantic_digest,export_scene\nfrom hearth.blocks import solid\nfrom .geometry import Polyline,Polygon\nfrom .sites import Platform\nfrom .interfaces import Endpoint\nfrom .circulation import Link\nfrom .boundaries import BoundaryChain,IlluminateBoundary\nfrom .halls import Pavilion\nfrom .programs import frozen_site,branching_campus,perimeter_cluster\n\nPROBE_DOMAIN=Box((-8,-4,-8),(45,52,45))\n\n\ndef probe_expression(environment_seed):\n    rng=Scope(environment_seed).stream('probe-field')\n    return (gradient(rng.choice((-.025,0,.035)),rng.choice((0,.015)),2)+wave(rng.choice((0,.35,.7)),.11,.09,rng.random()*6)+radial(16,18,14,-rng.choice((0,1,2)))).clamp(-1,5),rng.choice((None,3))\n\n\ndef freeze_probe(environment_seed):\n    field,water=probe_expression(environment_seed)\n    s=Scene(environment_seed,domain=PROBE_DOMAIN)\n    s.place('land',Terrain(s.domain,field,water))\n    return s.finalize()\n\n\ndef connector_scene(environment,structure_seed):\n    s=environment.fork(seed=structure_seed)\n    rng=Scope(structure_seed).stream('connection-intent')\n    s.place('a',Platform('/land'))\n    s.place('b',Platform('/land'),frame=Frame((18,0,27)))\n    a,b=Endpoint('/a','south'),Endpoint('/b','north')\n    link=Link(a,b,'/land',via=((rng.choice((9,12,15)),rng.choice((16,18,20))),),width=rng.choice((3,5)),rise=rng.choice((0,1,2)),covered=rng.choice((True,False)))\n    s.place('link',link,bindings=(a.binding(),b.binding()))\n    return s.finalize()\n\n\ndef boundary_scene(environment,structure_seed):\n    rng=Scope(structure_seed).stream('boundary-intent')\n    controls=((0,0),(8,rng.choice((0,10,23))),(28,rng.choice((0,5,18))),(37,18))\n    curve=Polyline(((0,0),(37,18))) if structure_seed==0 else Polyline.bezier(controls)\n    s=environment.fork(seed=structure_seed)\n    s.place('boundary',BoundaryChain(curve,height=rng.choice((1,2,3)),spacing=rng.choice((4,7,10))))\n    s.place('lights',IlluminateBoundary('/boundary'))\n    return s.finalize()\n\n\ndef hall_scene(seed,environment_seed=None):\n    rng=Scope(seed).stream('hall-intent')\n    domain=Box((-12,-4,-12),(42,65,42))\n    es=seed if environment_seed is None else environment_seed\n    field=gradient(.012,-.008,4)+wave(.5,.1,.12,Scope(es).stream('hall-field').random()*6)\n    env=Scene(es,domain=domain);env.place('land',Terrain(domain,field));env.finalize()\n    s=env.fork(seed=seed)\n    shape=Polygon.chamfered(rng.choice((13,17,21,25)),rng.choice((13,17,21)),rng.choice((0,2,4)))\n    s.place('hall',Pavilion(shape,'/land',rng.choice(('library','workshop','living')),('north','south','east') if rng.choice((True,False)) else ('north','west'),height=rng.choice((5,6,7)),roof_run=rng.choice((2,3)),lantern=rng.choice((True,False))))\n    return s.finalize()\n\n\ndef signatures(scene):\n    \"\"\"Measure current semantic geometry, omitting cosmetic material and contact height.\"\"\"\n    halls=[];links=[];boundaries=[];organization=[]\n    for path,node in sorted(scene.nodes.items()):\n        d=node.contract.decisions\n        if node.type=='extension.pavilion':\n            footprint=sorted((node.frame.inverse(p)[0],node.frame.inverse(p)[2]) for p in scene.cells(path+'/floor'))\n            halls.append({'footprint':footprint,'height':d['height'],'function':d['purpose'],'entrances':d['entrances'],'roof_run':d['roof_run'],'lantern':d['lantern']})\n        if node.type in ('extension.bridge','extension.gallery'):\n            pts=[node.frame.inverse(p) for p in scene.cells(path+'/deck')]\n            x0,z0=d['center'][0]\n            links.append({'body':sorted((p[0]-x0,p[2]-z0) for p in pts),'width':d['width'],'covered':d['covered'],'rise':d['rise']})\n        if node.type=='extension.boundary-chain':\n            pts=[node.frame.inverse(p) for p in scene.cells(path,True) if 'boundary-lantern' not in scene.nodes[scene.inspect(p)['instance']].type]\n            x0=min(p[0] for p in pts);z0=min(p[2] for p in pts)\n            boundaries.append(sorted(set((p[0]-x0,p[2]-z0) for p in pts)))\n        if node.type=='extension.campus':\n            vertex_keys=d['vertices']\n            vertices=[]\n            for key in vertex_keys:\n                v=scene.nodes[path+'/'+key]\n                at=node.frame.inverse(v.frame.origin)\n                vertices.append((at[0],at[2],v.capability.tags))\n            topology=sorted((vertex_keys.index(a),pa,vertex_keys.index(b),pb,covered,width) for a,pa,b,pb,covered,width in d['edges'])\n            organization.append({'positions':vertices,'topology':topology})\n    from hearth.assessment import principal_signature\n    legacy=principal_signature(scene)[1]['buildings']\n    principal={'halls':halls,'links':links,'boundaries':boundaries,'organization':organization,'legacy':legacy}\n    contact=[];fill=0;replaced=0;excavated=0\n    for path,node in sorted(scene.nodes.items()):\n        if node.type in ('extension.trestles','adapter.foundation','adapter.approach'):\n            contact.append((node.type,node.contract.decisions))\n            fill+=len(scene.cells(path))\n            replaced+=len(scene.displaced.get(path,{}))\n            excavated+=sum(solid(old.state) and (p not in scene.blocks or not solid(scene.blocks[p].state)) for p,old in scene.displaced.get(path,{}).items())\n    digest=lambda x:hashlib.sha256(json.dumps(x,sort_keys=True,default=list).encode()).hexdigest()\n    return {'principal_signature':digest(principal),'structure':principal,'contact_signature':digest(contact),'contact_cells':fill,'contact_replacements':replaced,'excavated_solid_cells':excavated}\n\n\ndef measured(label,recipe,inputs,artifact=None):\n    start=time.perf_counter()\n    row={'label':label,'inputs':inputs,'candidate_attempts':1,'accepted_candidates':0,'retries':0}\n    try:\n        scene=recipe();scene.finalize()\n        row.update(status='validated',accepted_candidates=1,blocks=len(scene.blocks),instances=len(scene.nodes),digest=semantic_digest(scene),**signatures(scene))\n        if artifact:\n            export_scene(scene,artifact)\n            row['artifact']=str(artifact)\n    except ContractError as exc:\n        limits=('site-relief','support-height','connection-support-height','connection-fill-limit','boundary-grade','endpoint-grade','approach-detour','connection-domain')\n        row.update(status='budget-exhausted' if isinstance(exc,SearchExhausted) else ('incompatible' if exc.diagnostic.rule in limits else 'defect'),diagnostic=exc.diagnostic.__dict__,all_diagnostics=[d.__dict__ for d in getattr(exc,'diagnostics',[exc.diagnostic])])\n    except Exception as exc:\n        row.update(status='defect',diagnostic={'exception':type(exc).__name__,'message':str(exc)})\n    row['seconds']=round(time.perf_counter()-start,4)\n    return row\n\n\ndef run(output,root_seed=0,artifacts=False):\n    output=Path(output);output.mkdir(parents=True,exist_ok=True)\n    rows=[];frozen={};inputs=[]\n    for es in range(root_seed*100,root_seed*100+8):\n        frozen[es]=freeze_probe(es)\n        field,water=probe_expression(es)\n        inputs.append({'environment_seed':es,'expression':field.expression(),'water':water,'digest':semantic_digest(frozen[es])})\n    (output/'frozen-inputs.json').write_text(json.dumps(inputs,indent=2))\n    def record(label,fn,parameters,keep=False):\n        path=Path('samples/extensions')/(label+'.litematic') if keep and artifacts else None\n        row=measured(label,fn,parameters,path);rows.append(row)\n        print(label,row['status'],flush=True)\n    for i,es in enumerate(frozen):\n        for bs in range(6):\n            record(f'connector-{i}-{bs}',lambda e=frozen[es],b=bs:connector_scene(e,b),{'environment':inputs[i],'structure_seed':bs},(i,bs) in ((0,0),(1,2),(4,3),(7,5)))\n        assert semantic_digest(frozen[es])==inputs[i]['digest']\n    for es in range(4):\n        # Independent dry substrates use the same field expression with water omitted.\n        field,_=probe_expression(es+root_seed*100)\n        env=Scene(es,domain=PROBE_DOMAIN);env.place('land',Terrain(env.domain,field));env.finalize()\n        for bs in range(4):\n            record(f'boundary-{es}-{bs}',lambda e=env,b=bs:boundary_scene(e,b),{'environment_seed':es+root_seed*100,'expression':field.expression(),'structure_seed':bs},(es,bs) in ((0,1),(3,3)))\n    for seed in range(12):\n        record(f'hall-{seed}',lambda b=seed:hall_scene(b,root_seed*100+b),{'structure_seed':seed,'environment_seed':root_seed*100+seed},seed in (0,4,7))\n    for name,program in (('campus',branching_campus),('perimeter',perimeter_cluster)):\n        for seed in range(6):\n            environment=frozen_site(root_seed*100+seed)\n            identity=semantic_digest(environment)\n            def recipe(e=environment,b=seed,p=program):\n                s=e.fork(seed=b);s.place('campus',p(b));return s.finalize()\n            record(f'{name}-{seed}',recipe,{'program':name,'structure_seed':seed,'environment_seed':root_seed*100+seed,'frozen_digest':identity},seed in (0,3))\n            assert semantic_digest(environment)==identity\n    for i in range(24):\n        es=2000+root_seed*100+i;rng=Scope(es).stream('stress')\n        field=(gradient(rng.choice((0,.12,.28)),rng.choice((0,-.12,.15)),2)+wave(rng.choice((0,2,4)),.13,.1,rng.random()*6)).clamp(-2,18)\n        water=rng.choice((None,3,9,12))\n        env=Scene(es,domain=PROBE_DOMAIN);env.place('land',Terrain(env.domain,field,water));env.finalize()\n        record(f'stress-{i:02}',lambda e=env,b=i%6:connector_scene(e,b),{'environment_seed':es,'structure_seed':i%6,'expression':field.expression(),'water':water})\n    (output/'results.json').write_text(json.dumps(rows,indent=2))\n    summary={}\n    for name in ('connector','boundary','hall','campus','perimeter','stress'):\n        part=[r for r in rows if r['label'].startswith(name+'-')];good=[r for r in part if r['status']=='validated']\n        summary[name]={'inputs':len(part),'outcomes':dict(Counter(r['status'] for r in part)),'candidate_attempts':sum(r['candidate_attempts'] for r in part),'accepted_candidates':sum(r['accepted_candidates'] for r in part),'retries':sum(r['retries'] for r in part),'principal_signatures':len({r['principal_signature'] for r in good}),'contact_signatures':len({r['contact_signature'] for r in good}),'contact_cells_range':[min((r['contact_cells'] for r in good),default=0),max((r['contact_cells'] for r in good),default=0)],'contact_replacement_range':[min((r['contact_replacements'] for r in good),default=0),max((r['contact_replacements'] for r in good),default=0)],'seconds':round(sum(r['seconds'] for r in part),3)}\n    (output/'summary.json').write_text(json.dumps(summary,indent=2))\n    if artifacts:\n        (Path('samples/extensions')/'replay-manifest.json').write_text(json.dumps([{k:r[k] for k in ('label','inputs','artifact','digest','status')} for r in rows if 'artifact' in r],indent=2))\n    return summary\nPY\ncat > examples/extension_assess.py <<'PY'\n# Rerun the retained extension sampling policy without altering first-phase evidence.\nimport argparse\nimport json\nfrom pathlib import Path\nfrom hearth_extensions.assessment import run\n\nif __name__=='__main__':\n    p=argparse.ArgumentParser()\n    p.add_argument('--seed',type=int,default=0)\n    p.add_argument('--output',type=Path,default=Path('reports/extension-corpus/final'))\n    p.add_argument('--artifacts',action='store_true')\n    a=p.parse_args()\n    print(json.dumps(run(a.output,a.seed,a.artifacts),indent=2))\nPY\nyapf -i hearth_extensions/assessment.py hearth_extensions/graphs.py examples/extension_assess.py\npython -m examples.extension_assess --seed 0 --output reports/extension-corpus/first-assessment --artifacts > reports/extension-corpus/first-assessment.log", "exit_code": 0, "seconds": 102, "output_tail": ""}
{"timestamp": "2026-09-26T22:54:55.660Z", "command": "python - <<'PY'\nimport json\nfrom pathlib import Path\nfor r in json.loads(Path('reports/extension-corpus/first-assessment/frozen-inputs.json').read_text()):\n print(r['environment_seed'],r['water'])\nPY\npython -m examples.extension_views --seed 0 --input samples/extensions/campus-0.litematic --output previews/extensions/campus-0 --size 1100 --sections > reports/extension-corpus/render-campus-0.txt", "exit_code": 0, "seconds": 17, "output_tail": ""}
{"timestamp": "2026-09-26T22:56:27.024Z", "command": "python - <<'PY'\nfrom pathlib import Path\np=Path('hearth/__init__.py');s=p.read_text().replace(\"'1.1.0'\",\"'1.2.0'\").replace('\"1.1.0\"','\"1.2.0\"');p.write_text(s)\np=Path('pyproject.toml');s=p.read_text().replace('version = \"1.1.0\"','version = \"1.2.0\"');p.write_text(s)\nPY\ncat > examples/extension_inspection.py <<'PY'\n# Inspect real extension structures and verify the same public queries after reload.\nimport argparse\nimport json\nfrom pathlib import Path\nimport hearth_extensions\nfrom hearth.persistence import load_scene,export_scene\n\n\ndef inspect(source,seed,output):\n    scene=load_scene(source)\n    if scene.seed!=seed:\n        raise ValueError('Explicit seed must match input artifact')\n    queries=[]\n    for tag in ('deck','window','fixture'):\n        candidate=next((n for n in scene.nodes.values() if n.capability and tag in n.capability.tags and scene.cells(n.path)),None)\n        if candidate is None:\n            continue\n        point=min(scene.cells(candidate.path))\n        queries.append({'tag':tag,'query':scene.inspect(point),'exact_cell_count':len(scene.cells(candidate.path)),'descendant_cell_count':len(scene.cells(candidate.path,True))})\n    export_scene(scene,output)\n    restored=load_scene(output)\n    for entry in queries:\n        assert restored.inspect(entry['query']['position'])==entry['query']\n        assert len(restored.cells(entry['query']['instance']))==entry['exact_cell_count']\n    print(json.dumps({'source':str(source),'seed':seed,'verified_after_reload':True,'queries':queries},default=lambda v:sorted(v),indent=2))\n\n\nif __name__=='__main__':\n    p=argparse.ArgumentParser()\n    p.add_argument('--seed',type=int,default=0)\n    p.add_argument('--input',type=Path,required=True)\n    p.add_argument('--output',type=Path,default=Path('samples/extensions/inspection.litematic'))\n    a=p.parse_args();inspect(a.input,a.seed,a.output)\nPY\nyapf -i hearth/__init__.py examples/extension_inspection.py\npython -m examples.extension_inspection --seed 0 --input samples/extensions/campus-0.litematic --output samples/extensions/inspection.litematic > reports/extension-corpus/inspection.json", "exit_code": 0, "seconds": 6, "output_tail": ""}
{"timestamp": "2026-09-26T22:56:38.590Z", "command": "tail -n 98 reports/extension-corpus/first-assessment.log", "exit_code": 0, "seconds": 0, "output_tail": "    },\n    \"candidate_attempts\": 16,\n    \"accepted_candidates\": 16,\n    \"retries\": 0,\n    \"principal_signatures\": 4,\n    \"contact_signatures\": 1,\n    \"contact_cells_range\": [\n      0,\n      0\n    ],\n    \"contact_replacement_range\": [\n      0,\n      0\n    ],\n    \"seconds\": 2.7\n  },\n  \"hall\": {\n    \"inputs\": 12,\n    \"outcomes\": {\n      \"validated\": 12\n    },\n    \"candidate_attempts\": 12,\n    \"accepted_candidates\": 12,\n    \"retries\": 0,\n    \"principal_signatures\": 12,\n    \"contact_signatures\": 12,\n    \"contact_cells_range\": [\n      38,\n      71\n    ],\n    \"contact_replacement_range\": [\n      6,\n      9\n    ],\n    \"seconds\": 7.862\n  },\n  \"campus\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      132,\n      206\n    ],\n    \"contact_replacement_range\": [\n      57,\n      126\n    ],\n    \"seconds\": 25.332\n  },\n  \"perimeter\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      82,\n      129\n    ],\n    \"contact_replacement_range\": [\n      21,\n      21\n    ],\n    \"seconds\": 23.338\n  },\n  \"stress\": {\n    \"inputs\": 24,\n    \"outcomes\": {\n      \"incompatible\": 9,\n      \"validated\": 15\n    },\n    \"candidate_attempts\": 24,\n    \"accepted_candidates\": 15,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 15,\n    \"contact_cells_range\": [\n      62,\n      259\n    ],\n    \"contact_replacement_range\": [\n      0,\n      254\n    ],\n    \"seconds\": 5.16\n  }\n}\n"}
{"timestamp": "2026-09-26T22:58:44.355Z", "command": "cat >> test/test_extension_geometry.py <<'PY'\n\n\ndef test_graph_parent_substitutes_public_access_components():\n    from hearth_extensions.graphs import Campus,Vertex,Edge\n    from hearth_extensions import Pavilion,Polygon\n    domain=Box((-12,-4,-12),(55,65,60))\n    variants=[]\n    for component in (Platform('/land',13,13),Pavilion(Polygon.chamfered(13,13,2),'/land',entrances=('north','south'))):\n        s=Scene(6,domain=domain)\n        s.place('land',Terrain(domain,gradient(base=3),water_level=3))\n        campus=Campus((Vertex('a',component,Frame()),Vertex('b',Platform('/land'),Frame((25,0,29)))),(Edge('a','south','b','north'),),'/land',domain,landscape=False)\n        s.place('group',campus)\n        s.finalize()\n        assert len(s.view.offers('/group','access'))==1\n        assert ('/group/circulation/link-0','connects','/group/a') in s.relations\n        variants.append(semantic_digest(s))\n    assert variants[0]!=variants[1]\n\n\n@pytest.mark.parametrize('environment_seed,structure_seed',[(0,0),(1,5),(2,3),(3,2),(4,1),(5,4),(6,0),(7,5)])\ndef test_independent_extension_environment_structure_pairs(environment_seed,structure_seed):\n    from hearth_extensions.assessment import freeze_probe,connector_scene\n    frozen=freeze_probe(environment_seed)\n    digest=semantic_digest(frozen)\n    scene=connector_scene(frozen,structure_seed)\n    assert semantic_digest(frozen)==digest\n    assert not validate(scene,True)\nPY\ncat >> test/test_extension_protocol.py <<'PY'\n\n\ndef test_importable_generation_has_no_new_native_dependency():\n    import subprocess,sys\n    from pathlib import Path\n    code='''\nimport sys,importlib.abc\nclass NoNative(importlib.abc.MetaPathFinder):\n def find_spec(self, fullname, path=None, target=None):\n  if fullname.split('.')[0] in ('numpy','mcio','scipy','shapely','ctypes'):\n   raise RuntimeError('Unexpected native/transport dependency: '+fullname)\nsys.meta_path.insert(0,NoNative())\nfrom hearth_extensions.assessment import hall_scene\nfrom hearth.persistence import semantic_digest\nscene=hall_scene(4,1)\nprint(semantic_digest(scene))\n'''\n    result=subprocess.run([sys.executable,'-c',code],cwd=Path(__file__).parents[1],capture_output=True,text=True)\n    assert result.returncode==0,result.stderr\n    assert len(result.stdout.strip())==64\nPY\nyapf -i hearth_extensions/sites.py hearth_extensions/boundaries.py test/test_extension_geometry.py test/test_extension_protocol.py\npython -m pytest -q test/test_extension_geometry.py test/test_extension_protocol.py test/test_extension_failures.py > reports/extension-corpus/focused-final.txt", "exit_code": 0, "seconds": 22, "output_tail": ""}
{"timestamp": "2026-09-26T22:59:55.368Z", "command": "cat reports/extension-corpus/focused-final.txt\ncat reports/extension-corpus/first-assessment/summary.json", "exit_code": 0, "seconds": 0, "output_tail": "\": 6,\n    \"contact_signatures\": 48,\n    \"contact_cells_range\": [\n      3,\n      108\n    ],\n    \"contact_replacement_range\": [\n      0,\n      100\n    ],\n    \"seconds\": 13.111\n  },\n  \"boundary\": {\n    \"inputs\": 16,\n    \"outcomes\": {\n      \"validated\": 16\n    },\n    \"candidate_attempts\": 16,\n    \"accepted_candidates\": 16,\n    \"retries\": 0,\n    \"principal_signatures\": 4,\n    \"contact_signatures\": 1,\n    \"contact_cells_range\": [\n      0,\n      0\n    ],\n    \"contact_replacement_range\": [\n      0,\n      0\n    ],\n    \"seconds\": 2.7\n  },\n  \"hall\": {\n    \"inputs\": 12,\n    \"outcomes\": {\n      \"validated\": 12\n    },\n    \"candidate_attempts\": 12,\n    \"accepted_candidates\": 12,\n    \"retries\": 0,\n    \"principal_signatures\": 12,\n    \"contact_signatures\": 12,\n    \"contact_cells_range\": [\n      38,\n      71\n    ],\n    \"contact_replacement_range\": [\n      6,\n      9\n    ],\n    \"seconds\": 7.862\n  },\n  \"campus\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      132,\n      206\n    ],\n    \"contact_replacement_range\": [\n      57,\n      126\n    ],\n    \"seconds\": 25.332\n  },\n  \"perimeter\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      82,\n      129\n    ],\n    \"contact_replacement_range\": [\n      21,\n      21\n    ],\n    \"seconds\": 23.338\n  },\n  \"stress\": {\n    \"inputs\": 24,\n    \"outcomes\": {\n      \"incompatible\": 9,\n      \"validated\": 15\n    },\n    \"candidate_attempts\": 24,\n    \"accepted_candidates\": 15,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 15,\n    \"contact_cells_range\": [\n      62,\n      259\n    ],\n    \"contact_replacement_range\": [\n      0,\n      254\n    ],\n    \"seconds\": 5.16\n  }\n}"}
{"timestamp": "2026-09-26T23:01:45.035Z", "command": "yapf -i hearth_extensions/circulation.py hearth_extensions/assessment.py\npython -m pytest -q test > reports/extension-corpus/full-regression.txt", "exit_code": 0, "seconds": 122, "output_tail": ""}
{"timestamp": "2026-09-26T23:02:04.748Z", "command": "cat > examples/extension_replay.py <<'PY'\n# Cross-process extension replay with full semantic graph and query comparisons.\nimport argparse\nimport json\nimport os\nfrom pathlib import Path\nimport subprocess\nimport sys\nimport tempfile\n\nCHILD = '''\nimport sys,json,hashlib\nfrom pathlib import Path\nfrom hearth_extensions.programs import extension_scene,perimeter_cluster\nfrom hearth_extensions.assessment import signatures\nfrom hearth.persistence import export_scene,load_scene,semantic_digest,encode\nscene=extension_scene(perimeter_cluster,int(sys.argv[1]),int(sys.argv[2]))\np=next(p for p,c in sorted(scene.blocks.items()) if c.state=='minecraft:glass' and 'facade' in c.owner)\nquery=scene.inspect(p)\nnodes=json.dumps(encode(scene.nodes),sort_keys=True,separators=(',',':'))\noutput=Path(sys.argv[3]);export_scene(scene,output)\nrestored=load_scene(output)\nassert restored.inspect(p)==query\nassert encode(restored.nodes)==encode(scene.nodes)\nprint(json.dumps({'digest':semantic_digest(scene),'principal':signatures(scene)['principal_signature'],'nodes_digest':hashlib.sha256(nodes.encode()).hexdigest(),'nodes':len(scene.nodes),'glass_query':query},default=lambda v:sorted(v),sort_keys=True))\n'''\n\n\ndef replay(seed,environment_seed,output):\n    root=Path(__file__).resolve().parents[1]\n    rows=[]\n    with tempfile.TemporaryDirectory(dir=root/'test',prefix='tmp-extension-replay-') as temporary:\n        for hash_seed in ('5','918273'):\n            artifact=Path(temporary)/f'seed {seed} hash {hash_seed}.litematic'\n            result=subprocess.run([sys.executable,'-c',CHILD,str(seed),str(environment_seed),str(artifact)],cwd=root,env=dict(os.environ,PYTHONHASHSEED=hash_seed),capture_output=True,text=True,check=True)\n            rows.append({'PYTHONHASHSEED':hash_seed,'result':json.loads(result.stdout)})\n    if rows[0]['result']!=rows[1]['result']:\n        raise AssertionError('Extension replay mismatch')\n    output.parent.mkdir(parents=True,exist_ok=True)\n    output.write_text(json.dumps({'verified':True,'seed':seed,'environment_seed':environment_seed,'runs':rows},indent=2))\n    print('Verified extension content, nodes, signatures, glass query and paired reload across two Python hash seeds.')\n\n\nif __name__=='__main__':\n    p=argparse.ArgumentParser()\n    p.add_argument('--seed',type=int,default=3)\n    p.add_argument('--environment-seed',type=int,default=7)\n    p.add_argument('--output',type=Path,default=Path('reports/extension-corpus/replay.json'))\n    a=p.parse_args();replay(a.seed,a.environment_seed,a.output)\nPY\nyapf -i examples/extension_replay.py\nset -e\npython -m examples.extension_replay --seed 3 --environment-seed 7 --output reports/extension-corpus/replay.json > reports/extension-corpus/replay.log\npython -m examples.verify_replay --seed 0 --output reports/extension-corpus/original-default-replay.json > reports/extension-corpus/original-default-replay.log", "exit_code": 0, "seconds": 45, "output_tail": ""}
{"timestamp": "2026-09-26T23:02:09.189Z", "command": "python -m examples.extension_assess --seed 0 --output reports/extension-corpus/final --artifacts > reports/extension-corpus/final-assessment.log", "exit_code": 0, "seconds": 105, "output_tail": ""}
{"timestamp": "2026-09-26T23:04:21.862Z", "command": "pwd && rg --files reports/extension-corpus examples | sort", "exit_code": 0, "seconds": 0, "output_tail": "ension_generate.cpython-312.pyc\nexamples/__pycache__/extension_inspection.cpython-312.pyc\nexamples/__pycache__/extension_replay.cpython-312.pyc\nexamples/__pycache__/extension_views.cpython-312.pyc\nexamples/__pycache__/preview.cpython-312.pyc\nexamples/__pycache__/verify_replay.cpython-312.pyc\nexamples/assess.py\nexamples/building.py\nexamples/courtyard.py\nexamples/extension.py\nexamples/extension_assess.py\nexamples/extension_generate.py\nexamples/extension_inspection.py\nexamples/extension_replay.py\nexamples/extension_views.py\nexamples/gallery.py\nexamples/inspection.py\nexamples/preview.py\nexamples/prototype.py\nexamples/verify_replay.py\nreports/extension-corpus/campus-development.txt\nreports/extension-corpus/client-probes-2.txt\nreports/extension-corpus/client-probes-3.txt\nreports/extension-corpus/client-probes.txt\nreports/extension-corpus/development-render.txt\nreports/extension-corpus/final-assessment.log\nreports/extension-corpus/final/frozen-inputs.json\nreports/extension-corpus/final/results.json\nreports/extension-corpus/final/summary.json\nreports/extension-corpus/first-assessment.log\nreports/extension-corpus/first-assessment/frozen-inputs.json\nreports/extension-corpus/first-assessment/results.json\nreports/extension-corpus/first-assessment/summary.json\nreports/extension-corpus/first-geometry-probes.txt\nreports/extension-corpus/focused-final.txt\nreports/extension-corpus/focused-tests-2.txt\nreports/extension-corpus/focused-tests.txt\nreports/extension-corpus/full-regression.txt\nreports/extension-corpus/geometry-probes-2.txt\nreports/extension-corpus/geometry-probes-3.txt\nreports/extension-corpus/inspection.json\nreports/extension-corpus/kernel-regression.txt\nreports/extension-corpus/original-default-replay.json\nreports/extension-corpus/original-default-replay.log\nreports/extension-corpus/render-campus-0.txt\nreports/extension-corpus/replay.json\nreports/extension-corpus/replay.log\nreports/extension-corpus/sampling-policy.json\nreports/extension-corpus/transforms-and-novelty.txt\n"}
{"timestamp": "2026-09-26T23:04:33.482Z", "command": "cat ARCHITECTURE.md; cat reports/extensions-baseline/api-delta.md", "exit_code": 0, "seconds": 0, "output_tail": "ction, capacity use and dependency without acquiring a mutation grant, whether or not the\nport also has a grant. All other verbs retain their original checks. A connection never authorizes\nwriting to the host. The extension cannot implement this using its own effects because binding\npermissions are enforced by the kernel before effects are applied; requesting unrelated mutation\npermission would misrepresent its authority. No component-name dispatch or signature change was added.\n\nThe regression suite adds a negative case proving that a connected consumer cannot overwrite the\nlanding. Old artifact structures and serialized contract schemas are unchanged. This is a self-authored\nextension/protocol exercise, not independent review.\n\n## Explicit replacements in a coupled replay\n\nThe bridge replacement test exposed conservative host invalidation: removing contact output can\ninvalidate earlier platform surveys even though their principal intent is unchanged. Replaying only\nthe link correctly refuses to finalize stale platform observations. Replaying hosts and link together\nwas possible, but the public API could only replace the first target, while the changed link must\ncome after its hosts. The proposed `replacements` call failed before the change; its TypeError is\nretained in `failing-replay-probe.txt`, and the original stale-input result remains in\n`../extension-corpus/focused-tests.txt`.\n\n`Scene.regenerate(..., dependents=(...), replacements={path: component, ...})` now accepts explicit\nreplacements for any named replay target. Targets remain explicitly ordered, retain their frames,\nscopes and bindings, and replay in one bounded transaction. Undeclared or duplicate targets fail.\nNo automatic graph-wide update loop or stale-flag bypass was added. A link can be replaced while its\nunchanged hosts are recomputed first from their inputs. Reloaded branches may likewise supply their\nimplementations explicitly through this mapping. Existing calls keep their previous behavior.\n"}
{"timestamp": "2026-09-26T23:04:33.490Z", "command": "cat reports/extension-corpus/full-regression.txt reports/extension-corpus/final/summary.json reports/extension-corpus/replay.json reports/extension-corpus/original-default-replay.json", "exit_code": 0, "seconds": 0, "output_tail": "        \"contained_by\",\n              \"/campus/market\"\n            ],\n            [\n              \"/campus/market/light-4-4\",\n              \"contained_by\",\n              \"/campus/market\"\n            ],\n            [\n              \"/campus/market/light-4-9\",\n              \"contained_by\",\n              \"/campus/market\"\n            ],\n            [\n              \"/campus/market/light-9-4\",\n              \"contained_by\",\n              \"/campus/market\"\n            ],\n            [\n              \"/campus/market/light-9-9\",\n              \"contained_by\",\n              \"/campus/market\"\n            ],\n            [\n              \"/campus/market/roof\",\n              \"contained_by\",\n              \"/campus/market\"\n            ],\n            [\n              \"/campus/market/shell\",\n              \"contained_by\",\n              \"/campus/market\"\n            ],\n            [\n              \"/campus/reading\",\n              \"contained_by\",\n              \"/campus\"\n            ]\n          ],\n          \"shared\": [],\n          \"state\": \"minecraft:glass\"\n        },\n        \"nodes\": 385,\n        \"nodes_digest\": \"7f03bb1230508adee058ac19020c0702a9f7d19c241963db19b9a14e703450c5\",\n        \"principal\": \"e582861768e396b4f060b4aabca0b55e914ddc89cbf104c85dc2411bcad265a7\"\n      }\n    }\n  ]\n}{\n  \"verified\": true,\n  \"runs\": [\n    {\n      \"seed\": 0,\n      \"PYTHONHASHSEED\": \"3\",\n      \"digest\": \"e81b08063278b231b60eaa16e4120973ed688bb3fd7e6a8da439daa4e7f5caf9\",\n      \"bounds\": [\n        [\n          -49,\n          -4,\n          -23\n        ],\n        [\n          49,\n          32,\n          104\n        ]\n      ],\n      \"blocks\": 125475,\n      \"buildings\": 5\n    },\n    {\n      \"seed\": 0,\n      \"PYTHONHASHSEED\": \"8675309\",\n      \"digest\": \"e81b08063278b231b60eaa16e4120973ed688bb3fd7e6a8da439daa4e7f5caf9\",\n      \"bounds\": [\n        [\n          -49,\n          -4,\n          -23\n        ],\n        [\n          49,\n          32,\n          104\n        ]\n      ],\n      \"blocks\": 125475,\n      \"buildings\": 5\n    }\n  ]\n}"}
{"timestamp": "2026-09-26T23:04:33.495Z", "command": "cat AGENTS.md /work/AGENTS.md; cat examples/extension_views.py; cat GENERATOR.md", "exit_code": 0, "seconds": 0, "output_tail": "ervative; no implicit arbitrary auto-replan |\n| R20 | SHA-256 scope keys/purposes, overrides, copied environments; branch/order/retry and hash-seed replay tests | Genuine spatial dependencies may change descendants |\n| R21 | Configurable `Limits`, `SiteLimits`, effect envelopes and realized export bounds | Demonstration count bound is only in the CLI |\n| R22 | Initial design, kernel tests, prototype, buildings/extension, corpus/render stages | Generic defects and fixes recorded in development findings |\n| R23 | `test_kernel`, `test_contracts_provenance`: substitution, independent extension, authority, rollback, sharing, transforms, seeds | Custom validator registration also tested |\n| R24 | Glass chain, two identities, external attachment membership, updates/deletions/regeneration, shared cells, transport mismatch tests | Actual structure restored from companion, not inferred from materials |\n| R25 | 64 frozen pairings, compatible independent field/environment, protected outside cells, perturbation and stress records | Finite supported domain; 32 stress inputs retained without resampling |\n| R26 | Three short clients x eight seeds; settlement x eight seeds plus counts 3 and 6; later courtyard/gallery | Programs use public room/component APIs |\n| R27 | Actual blocks/NBT: schema, support, headroom/routes, pair consistency, enclosure, lights, furnishings, pools and path joins; deliberate damage tests | Physical/lighting model limitations above apply |\n| R28 | Structural/contact signatures, exterior views, circulation/furnishing cutaways, inspection notes | Modular aesthetic remains visible; no exhaustive beauty claim |\n| R29 | `generate.py`, importable scene builders, inspection example, verified one-region transport | Offline runtime uses bundled schema and installed/provided MCIO |\n| R30 | Library, docs, CLI, tests, extension, output pair, manifest, reports and labeled previews | Broader simulation, arbitrary entities, browser UI and city planner remain unfinished |\n"}
{"timestamp": "2026-09-26T23:04:47.408Z", "command": "sed -n '1,240p' hearth_extensions/assessment.py; cat reports/extension-corpus/sampling-policy.json", "exit_code": 0, "seconds": 0, "output_tail": "hase\": \"extension\", \"version\": 1, \"root_seed\": 0,\n  \"frozen_before_assessment\": true,\n  \"connectors\": {\"environment_seeds\": [0,7], \"structure_seeds\": [0,5], \"count\": 48, \"policy\": \"Eight independently frozen generic terrain expressions, each copied for six width/rise/cover/curve decisions. Locked platforms and public endpoints. Gradients bounded by 0.035, waves 0..0.7, radial depression 0..2, optional water level 3. Width 3 or 5, rise 0..2, post height <=8, maximum unsupported taxicab deck span 6, fill <=800. No resampling.\"},\n  \"boundaries\": {\"count\": 16, \"policy\": \"Four environment seeds by four independently seeded cubic controls, height 1..3 and socket spacing 4..10. Include a zero-curvature line. Maximum base change one block per raster step.\"},\n  \"halls\": {\"count\": 12, \"policy\": \"Seeds 0..11, variable polygon widths/depths 13..25, chamfer 0..4, ceiling 5..7, hip or hip with lantern. Functions, entrances and proportions locked before site query.\"},\n  \"compositions\": {\"count\": 12, \"policy\": \"Two lightweight public graph clients, seeds 0..5. One branching campus, one perimeter cluster. Preserve locked vertices and declared graph edges. Includes existing Hearth buildings substituted for polygon halls.\"},\n  \"stress\": {\"count\": 24, \"policy\": \"Single locked endpoint pair per input. Independent environment seeds 2000..2023, gradients through 0.28, waves through 4, water through 12, profile rise 0..3. Retain every success and failure without resampling or widened bounds.\"},\n  \"novel_compositions\": \"Focused tests additionally use an unsampled kinked polyline and a polygon footprint not used in the two shipped clients.\",\n  \"metrics\": [\"input outcome\", \"candidate acceptance\", \"all diagnostics\", \"runtime\", \"actual edit cost\", \"principal geometry/topology signature\", \"contact signature\"],\n  \"claims\": \"Self-authored finite extension exercise, not independent audit. Prior external Pyodide results are user-reported; this phase claims only execution actually performed here.\"\n}\n"}
{"timestamp": "2026-09-26T23:04:47.418Z", "command": "cat /work/AGENTS.md; cat examples/extension_views.py; cat examples/extension_inspection.py", "exit_code": 0, "seconds": 0, "output_tail": "n__':\n    p = argparse.ArgumentParser()\n    p.add_argument('--seed', type=int, default=0)\n    p.add_argument('--input', type=Path, required=True)\n    p.add_argument('--output', type=Path, required=True)\n    p.add_argument('--size', type=int, default=1100)\n    p.add_argument('--sections', action='store_true')\n    a = p.parse_args()\n    views(a.seed, a.input, a.output, a.size, a.sections)\n# Inspect real extension structures and verify the same public queries after reload.\nimport argparse\nimport json\nfrom pathlib import Path\nimport hearth_extensions\nfrom hearth.persistence import load_scene, export_scene\n\n\ndef inspect(source, seed, output):\n    scene = load_scene(source)\n    if scene.seed != seed:\n        raise ValueError('Explicit seed must match input artifact')\n    queries = []\n    for tag in ('deck', 'window', 'fixture'):\n        candidate = next((n for n in scene.nodes.values() if n.capability and tag in n.capability.tags and scene.cells(n.path)), None)\n        if candidate is None:\n            continue\n        point = min(scene.cells(candidate.path))\n        queries.append({'tag': tag, 'query': scene.inspect(point), 'exact_cell_count': len(scene.cells(candidate.path)), 'descendant_cell_count': len(scene.cells(candidate.path, True))})\n    export_scene(scene, output)\n    restored = load_scene(output)\n    for entry in queries:\n        assert restored.inspect(entry['query']['position']) == entry['query']\n        assert len(restored.cells(entry['query']['instance'])) == entry['exact_cell_count']\n    print(json.dumps({'source': str(source), 'seed': seed, 'verified_after_reload': True, 'queries': queries}, default=lambda v: sorted(v), indent=2))\n\n\nif __name__ == '__main__':\n    p = argparse.ArgumentParser()\n    p.add_argument('--seed', type=int, default=0)\n    p.add_argument('--input', type=Path, required=True)\n    p.add_argument('--output', type=Path, default=Path('samples/extensions/inspection.litematic'))\n    a = p.parse_args()\n    inspect(a.input, a.seed, a.output)\n"}
{"timestamp": "2026-09-26T23:04:47.429Z", "command": "rg --files previews/extensions samples/extensions; sed -n '1,150p' GENERATOR.md; sed -n '1,230p' hearth_extensions/programs.py", "exit_code": 0, "seconds": 0, "output_tail": "outh', covered=True), Edge('hall', 'east', 'kitchen', 'west', rise=rng.choice((0, 1))), Edge('hall', 'south', 'lodge', 'street', covered=True)]\n    if rng.choice((False, True)):\n        vertices.append(Vertex('archive', Pavilion(Polygon.chamfered(13, 13, 2), '/land', 'library', ('east',)), Frame((0, 0, 51))))\n        edges.append(Edge('hall', 'west', 'archive', 'east', rise=1))\n    boundary = Polyline.bezier(((-12, 0), (-30, 120), (115, 120), (84, 0)))\n    return Campus(tuple(vertices), tuple(edges), '/land', DOMAIN, boundary)\n\n\ndef perimeter_cluster(seed=0):\n    rng = Scope(seed).stream('perimeter-graph')\n    widths = (rng.choice((17, 21, 25)), rng.choice((13, 17)), rng.choice((13, 17)))\n    market = Pavilion(Polygon.chamfered(widths[0], 13, 2), '/land', 'workshop', ('north', 'east', 'south'), height=5, roof_run=3)\n    reading = Pavilion(Polygon.chamfered(widths[1], 17, 3), '/land', 'library', ('north', 'south'), height=6, lantern=rng.choice((True, False)))\n    dining = Pavilion(Polygon.chamfered(widths[2], 13, 2), '/land', 'living', ('east', 'west'), height=5)\n    vertices = (Vertex('market', market, Frame((0, 0, 0))), Vertex('reading', reading, Frame((50, 0, 10))), Vertex('dining', dining, Frame((20, 0, 57))))\n    edges = (Edge('market', 'east', 'reading', 'north', via=((40, -5),), covered=False, rise=1), Edge('reading', 'south', 'dining', 'east', covered=True), Edge('dining', 'west', 'market', 'south', via=((9, 42),), rise=rng.choice((0, 1))))\n    boundary = Polyline.bezier(((-12, 0), (-30, 120), (115, 120), (84, 0)))\n    return Campus(vertices, edges, '/land', DOMAIN, boundary)\n\n\ndef extension_scene(program=branching_campus, seed=0, environment_seed=0):\n    environment = frozen_site(environment_seed)\n    original = semantic_digest(environment)\n    scene = environment.fork(seed=seed)\n    scene.place('campus', program(seed))\n    scene.finalize()\n    if semantic_digest(environment) != original:\n        raise AssertionError('Frozen input mutated')\n    return scene\n"}
{"timestamp": "2026-09-26T23:05:14.148Z", "command": "python -m examples.extension_views --seed 5 --input samples/extensions/connector-7-5.litematic --output previews/extensions/wet-connector --sections > reports/extension-corpus/render-wet-connector.txt 2>&1", "exit_code": 0, "seconds": 9, "output_tail": ""}
{"timestamp": "2026-09-26T23:05:23.823Z", "command": "python -m examples.extension_views --seed 3 --input samples/extensions/perimeter-3.litematic --output previews/extensions/perimeter-3 --sections > reports/extension-corpus/render-perimeter-3.txt 2>&1", "exit_code": 0, "seconds": 19, "output_tail": ""}
{"timestamp": "2026-09-26T23:05:23.934Z", "command": "sed -n '1,280p' hearth_extensions/halls.py; sed -n '1,260p' hearth_extensions/graphs.py", "exit_code": 0, "seconds": 0, "output_tail": "Error('graph-access', ctx.path, conditions='First vertex must expose usable access')\n        gateway = Port('public', 'access', port.frame, port.region, port.capacity, delegate=(first.key, port.key))\n        return Contract(self.envelope, ports=(gateway,), decisions={'vertices': [v.key for v in self.vertices], 'edges': [(e.a, e.a_port, e.b, e.b_port, e.covered, e.width) for e in self.edges]})\n\n    def realize(self, ctx, contract):\n        p = Plan(children=[Child(v.key, v.component, frame=v.frame) for v in self.vertices])\n        p.children.append(Child('circulation', Connections(ctx.path, self.edges, self.terrain, self.envelope)))\n        if self.boundary is not None:\n            p.children.extend((Child('boundary', BoundaryChain(self.boundary)), Child('boundary-lights', IlluminateBoundary(ctx.path + '/boundary', 2))))\n        if self.landscape:\n            p.children.append(Child('landscape', Landscape(self.envelope, self.terrain, 8, 12, 0.025)))\n        return p\n\n\n@dataclass(frozen=True)\nclass Connections:\n    parent: str\n    edges: tuple[Edge, ...]\n    terrain: str\n    envelope: Box\n\n    def capability(self):\n        return Capability('extension.connection-graph', ('connection', 'composite'))\n\n    def negotiate(self, ctx, parameters):\n        targets = []\n        for edge in self.edges:\n            for key, port in ((edge.a, edge.a_port), (edge.b, edge.b_port)):\n                targets.append(ctx.local(ctx.view.port(self.parent + '/' + key, port).frame.origin))\n        return Contract(self.envelope, rules=(Rule('route', tuple(targets), self.envelope, phase='complete'),))\n\n    def realize(self, ctx, contract):\n        p = Plan()\n        for i, edge in enumerate(self.edges):\n            a = Endpoint(self.parent + '/' + edge.a, edge.a_port)\n            b = Endpoint(self.parent + '/' + edge.b, edge.b_port)\n            p.children.append(connection(f'link-{i}', a, b, self.terrain, via=edge.via, covered=edge.covered, rise=edge.rise, width=edge.width))\n        return p\n"}
{"timestamp": "2026-09-26T23:05:47.298Z", "command": "sed -n '1,150p' examples/verify_replay.py; cat hearth_extensions/__init__.py; cat reports/extensions-baseline/module-hashes.json; sed -n '1,130p' hearth/persistence.py; cat pyproject.toml; cat reports/extension-corpus/final-assessment.log", "exit_code": 0, "seconds": 0, "output_tail": "   \"contact_signatures\": 48,\n    \"contact_cells_range\": [\n      3,\n      108\n    ],\n    \"contact_replacement_range\": [\n      0,\n      100\n    ],\n    \"seconds\": 13.313\n  },\n  \"boundary\": {\n    \"inputs\": 16,\n    \"outcomes\": {\n      \"validated\": 16\n    },\n    \"candidate_attempts\": 16,\n    \"accepted_candidates\": 16,\n    \"retries\": 0,\n    \"principal_signatures\": 4,\n    \"contact_signatures\": 16,\n    \"contact_cells_range\": [\n      0,\n      0\n    ],\n    \"contact_replacement_range\": [\n      0,\n      0\n    ],\n    \"seconds\": 2.779\n  },\n  \"hall\": {\n    \"inputs\": 12,\n    \"outcomes\": {\n      \"validated\": 12\n    },\n    \"candidate_attempts\": 12,\n    \"accepted_candidates\": 12,\n    \"retries\": 0,\n    \"principal_signatures\": 12,\n    \"contact_signatures\": 12,\n    \"contact_cells_range\": [\n      38,\n      71\n    ],\n    \"contact_replacement_range\": [\n      6,\n      9\n    ],\n    \"seconds\": 8.02\n  },\n  \"campus\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      132,\n      206\n    ],\n    \"contact_replacement_range\": [\n      232,\n      318\n    ],\n    \"seconds\": 25.974\n  },\n  \"perimeter\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      82,\n      129\n    ],\n    \"contact_replacement_range\": [\n      256,\n      451\n    ],\n    \"seconds\": 24.337\n  },\n  \"stress\": {\n    \"inputs\": 24,\n    \"outcomes\": {\n      \"incompatible\": 9,\n      \"validated\": 15\n    },\n    \"candidate_attempts\": 24,\n    \"accepted_candidates\": 15,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 15,\n    \"contact_cells_range\": [\n      62,\n      259\n    ],\n    \"contact_replacement_range\": [\n      0,\n      254\n    ],\n    \"seconds\": 5.268\n  }\n}\n"}
{"timestamp": "2026-09-26T23:06:04.704Z", "command": "python -m examples.extension_views --seed 3 --input samples/extensions/boundary-3-3.litematic --output previews/extensions/curve-boundary > reports/extension-corpus/render-curve-boundary.txt 2>&1", "exit_code": 0, "seconds": 5, "output_tail": ""}
{"timestamp": "2026-09-26T23:06:07.280Z", "command": "python -m examples.extension_views --seed 4 --input samples/extensions/hall-4.litematic --output previews/extensions/hall-4 --sections > reports/extension-corpus/render-hall-4.txt 2>&1", "exit_code": 0, "seconds": 7, "output_tail": ""}
{"timestamp": "2026-09-26T23:06:09.111Z", "command": "sed -n '1,280p' hearth_extensions/circulation.py; sed -n '1,180p' test/test_extension_failures.py", "exit_code": 0, "seconds": 0, "output_tail": "20), (26, 0), (37, 17)))\n    s.place('boundary', BoundaryChain(curve))\n    s.place('lamps', IlluminateBoundary('/boundary'))\n    before = snapshot(s)\n    with pytest.raises(ContractError):\n        s.remove('/boundary')\n    assert snapshot(s) == before\n    changed = Polyline.bezier(((0, 1), (9, 19), (26, 1), (37, 18)))\n    s.regenerate('/boundary', BoundaryChain(changed), dependents=('/lamps',))\n    s.finalize()\n    p = s.view.offers('/boundary', 'mount')[0].frame.origin\n    assert p in s.cells('/boundary', True)\n    export_scene(s, work / 'curve.litematic')\n    t = load_scene(work / 'curve.litematic')\n    assert t.inspect(p) == s.inspect(p)\n    base = (p[0], p[1] - 1, p[2])\n    damage(t, base)\n    assert any(d.rule in ('support', 'enclosure') and d.position == base for d in validate(t, True))\n\n\ndef test_curve_boundary_rejects_incompatible_installation_frame():\n    from hearth_extensions import BoundaryLantern\n    s = Scene(0, domain=Box((-3, -4, -3), (25, 25, 25)))\n    s.place('land', Terrain(s.domain, gradient(base=3)))\n    s.place('wall', Boundary(Polyline(((0, 0), (20, 8)))))\n    port = s.view.offers('/wall', 'mount')[0]\n    before = snapshot(s)\n    with pytest.raises(ContractError, match='port-alignment'):\n        s.place('bad-lamp', BoundaryLantern(), frame=Frame(port.frame.origin, port.frame.turn + 1), bindings=(Binding('/wall', port.key),))\n    assert snapshot(s) == before\n\n\ndef test_failed_choice_does_not_perturb_sibling_randomness():\n    s = bridge_scene()\n    s.remove('/link')\n    a, b = Endpoint('/a', 'south'), Endpoint('/b', 'north')\n    before = snapshot(s)\n    bad = Link(a, b, '/land', fill_limit=0)\n    with pytest.raises(ContractError):\n        s.compose_choice(Choice('link', (Child('attempt', bad, bindings=(a.binding(), b.binding())),), lambda v, p: 0))\n    assert snapshot(s) == before\n    good = Link(a, b, '/land', rise=1)\n    s.place('link', good, bindings=(a.binding(), b.binding()))\n    assert semantic_digest(s) == semantic_digest(bridge_scene())\n"}
{"timestamp": "2026-09-26T23:06:49.657Z", "command": "sed -n '130,280p' hearth/persistence.py; sed -n '150,310p' GENERATOR.md; cat test/test_extension_protocol.py", "exit_code": 0, "seconds": 0, "output_tail": "write-authority'):\n        s.place('bad', Bad(endpoint), bindings=(endpoint.binding(),))\n    assert s.blocks == before and '/bad' not in s.nodes\n\n\ndef test_atomic_multi_branch_replay_accepts_explicit_replacements():\n    from hearth.components.primitives import Volume\n    s = Scene()\n    s.place('a', Volume(Box((0, 0, 0), (0, 0, 0)), state_of('stone')))\n    s.place('b', Volume(Box((3, 0, 0), (3, 0, 0)), state_of('stone')))\n    s.regenerate('/a', dependents=('/b',), replacements={'/b': Volume(Box((3, 0, 0), (4, 0, 0)), state_of('oak_planks'))})\n    assert len(s.cells('/b')) == 2\n    assert s.inspect((0, 0, 0))['state'] == state_of('stone')\n\n\ndef test_multi_branch_replay_rejects_undeclared_target_and_rolls_back():\n    from hearth.components.primitives import Volume\n    from hearth.kernel import ContractError\n    from hearth.persistence import semantic_digest\n    s = Scene()\n    s.place('a', Volume(Box((0, 0, 0), (0, 0, 0)), state_of('stone')))\n    before = semantic_digest(s)\n    with pytest.raises(ContractError, match='replay-targets'):\n        s.regenerate('/a', replacements={'/unknown': Landing()})\n    assert semantic_digest(s) == before and set(s.nodes) == {'/a'}\n\n\ndef test_importable_generation_has_no_new_native_dependency():\n    import subprocess, sys\n    from pathlib import Path\n    code = '''\nimport sys,importlib.abc\nclass NoNative(importlib.abc.MetaPathFinder):\n def find_spec(self, fullname, path=None, target=None):\n  if fullname.split('.')[0] in ('numpy','mcio','scipy','shapely','ctypes'):\n   raise RuntimeError('Unexpected native/transport dependency: '+fullname)\nsys.meta_path.insert(0,NoNative())\nfrom hearth_extensions.assessment import hall_scene\nfrom hearth.persistence import semantic_digest\nscene=hall_scene(4,1)\nprint(semantic_digest(scene))\n'''\n    result = subprocess.run([sys.executable, '-c', code], cwd=Path(__file__).parents[1], capture_output=True, text=True)\n    assert result.returncode == 0, result.stderr\n    assert len(result.stdout.strip()) == 64\n"}
{"timestamp": "2026-09-26T23:08:16.764Z", "command": "cat examples/extension_assess.py; sed -n '1,240p' hearth_extensions/geometry.py; sed -n '1,230p' hearth_extensions/boundaries.py", "exit_code": 0, "seconds": 0, "output_tail": "4)\n            p.children.append(Child(port.key, BoundaryLantern(), frame=frame, bindings=(Binding(self.host, port.key),)))\n        return p\n\n\n@dataclass(frozen=True)\nclass BoundaryChain:\n    curve: Polyline\n    height: int = 2\n    spacing: int = 7\n    chunk: int = 96\n\n    def capability(self):\n        return Capability('extension.boundary-chain', ('boundary', 'composite'), offers=('mount',), inputs={'chunk': Domain(4, 220), 'height': Domain(1, 5), 'spacing': Domain(4, 12)}, guarantees=('continuous contour across component joins',))\n\n    def parts(self):\n        cells = self.curve.cells()\n        if len(set(cells)) != len(cells):\n            raise ContractError('boundary-self-intersection', conditions='A simple open contour is required')\n        count = max(1, (len(cells) + self.chunk - 1) // self.chunk)\n        size = (len(cells) + count - 1) // count\n        return tuple(Boundary(Polyline(cells[i:i + size]), self.height, self.spacing) for i in range(0, len(cells), size))\n\n    def negotiate(self, ctx, parameters):\n        boxes = []\n        ports = []\n        last_height = None\n        for i, part in enumerate(self.parts()):\n            local = part.negotiate(ctx, {})\n            if last_height is not None and abs(local.decisions['levels'][0] - last_height) > part.max_step:\n                raise ContractError('boundary-grade', ctx.path, conditions='Grade bound also applies at component joins')\n            last_height = local.decisions['levels'][-1]\n            boxes.append(local.envelope)\n            for p in local.ports:\n                ports.append(Port(f'segment-{i}-{p.key}', p.kind, p.frame, p.region, p.capacity, facts=p.facts, delegate=(f'segment-{i}', p.key)))\n        return Contract(Box.enclosing(p for box in boxes for p in box.corners()), ports=tuple(ports), decisions={'segments': len(boxes), 'curve': self.curve.points})\n\n    def realize(self, ctx, contract):\n        return Plan(children=[Child(f'segment-{i}', part) for i, part in enumerate(self.parts())])\n"}
{"timestamp": "2026-09-26T23:08:41.297Z", "command": "yapf -i examples/extension_audit.py && python -m examples.extension_audit --seed 0 --cli > reports/extension-corpus/artifact-audit.log 2>&1", "exit_code": 0, "seconds": 49, "output_tail": ""}
{"timestamp": "2026-09-26T23:08:54.750Z", "command": "sed -n '1,90p' hearth/kernel/contracts.py; cat reports/extension-corpus/artifact-audit.log; tail -8 reports/extension-corpus/render-hall-4.txt; sed -n '1,80p' hearth_extensions/sites.py; git status --short", "exit_code": 128, "seconds": 0, "output_tail": "locks import state_of\nfrom hearth.adapters.site import Foundation, SiteLimits, survey\nfrom .geometry import Polygon, bounds2\nfrom .circulation import Surface\n\nDIRECTIONS = {'north': (0, -1, 0), 'east': (1, 0, 1), 'south': (0, 1, 2), 'west': (-1, 0, 3)}\n\n\ndef edge_frames(width, depth, height):\n    return {'north': Frame((width // 2, height, 0), 0), 'east': Frame((width - 1, height, depth // 2), 1), 'south': Frame((width // 2, height, depth - 1), 2), 'west': Frame((0, height, depth // 2), 3)}\n\n\n@dataclass(frozen=True)\nclass Platform:\n    terrain: str\n    width: int = 9\n    depth: int = 9\n    freeboard: int = 1\n\n    def capability(self):\n        return Capability('extension.platform', ('platform', 'composite', 'access'), offers=('access',), inputs={'width': Domain(5, 27), 'depth': Domain(5, 27), 'freeboard': Domain(1, 3)})\n\n    def negotiate(self, ctx, parameters):\n        footprint = tuple((x, z) for x in range(self.width) for z in range(self.depth))\n        samples = survey(ctx, footprint)\n        y = samples['floor'] + self.freeboard - 1\n        region = bounds2(footprint, min(v[0] for v in samples['samples'].values()) - 1, y + 3)\n        ports = tuple(Port(k, 'access', f, f.box(Box((-2, -1, 0), (2, 1, 0))), 8) for k, f in edge_frames(self.width, self.depth, y + 1).items())\n        return Contract(region, ports=ports, decisions={'floor': y, 'footprint': footprint, 'strategy': samples['strategy']}, rules=(Rule('route', tuple(f.frame.origin for f in ports), region, phase='complete'),))\n\n    def realize(self, ctx, contract):\n        d = contract.decisions\n        p = Plan(children=[Child('foundation', Foundation(tuple(d['footprint']), d['floor'], d['strategy']), bindings=(Binding(self.terrain, 'construction', 'fill'),)), Child('deck', Surface(tuple((x, d['floor'], z) for x, z in d['footprint']), role='platform'))])\n        return p\nfatal: not a git repository (or any parent up to mount point /)\nStopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).\n"}
{"timestamp": "2026-09-26T23:09:13.087Z", "command": "rg -n 'class Furniture|def realize|self.purpose|chair' hearth/components/interior.py; sed -n '110,230p' hearth/components/interior.py; python - <<'PY'\nimport json\ns=json.load(open('samples/extensions/hall-4.litematic.structure.json'))\nprint(s['nodes']['/hall']['contract']['decisions']['purpose'])\nprint(s['nodes']['/hall/furnishings']['contract'])\nPY", "exit_code": 0, "seconds": 0, "output_tail": "ide in self.windows:\n                bays = (2, length - 4) if length >= 11 else ((1, length - 3) if length >= 9 else ((length - 2) // 2,))\n                for i, bay in enumerate(bays):\n                    if side in self.doors and abs(bay - length // 2) < 2:\n                        continue\n                    at = frame.compose(Frame((bay, 2, 0)))\n                    p.children.append(Child(f'window-{side}-{i}', self.window_component or Window(), frame=at, bindings=(Binding(wallpath, 'infill'),)))\n        # Furnish the opposite half to the flight; clipping stays inside the furnishing component's declared domain.\n        furniture = Furniture(self.purpose, w, d)\n        if self.stair_up or self.stair_down:\n            furniture = Furniture(self.purpose, w - 3, d)\n        p.children.append(Child('furniture', furniture, frame=Frame((0, 1, 0))))\n        for i, (x, z) in enumerate(((2, d - 3), (w - 3, 2))):\n            if self.stair_up and i == 1:\n                x = w // 2\n            p.children.append(Child(f'light-{i}', self.light_component or Light(), frame=Frame((x, 4, z))))\n        from .landscape import FacadeGarden\n        p.children.append(Child('facade-garden', FacadeGarden(w, d, tuple(s for s in self.windows if s not in self.doors))))\n        if self.stair_up:\n            p.children.append(Child('stairs', Stair(), frame=Frame((w - 4, 1, 2))))\n        return p\n\n\n@dataclass(frozen=True)\nclass Pattern:\n    role: str\n    envelope: Box\n    writes: tuple\n\n    def capability(self):\n        return Capability('surface.' + self.role, (self.role,))\n\n    def negotiate(self, ctx, parameters):\n        return Contract(self.envelope)\n\n    def realize(self, ctx, contract):\n        return Plan(writes=list(self.writes))\nliving\n{'$type': 'Contract', 'atomic_object': False, 'decisions': {}, 'envelope': {'$type': 'Box', 'hi': {'$tuple': [16, 8, 8]}, 'lo': {'$tuple': [4, 6, 4]}}, 'grants': {'$tuple': []}, 'ports': {'$tuple': []}, 'reads': {'$tuple': []}, 'rules': {'$tuple': []}}\n"}
{"timestamp": "2026-09-26T23:09:18.506Z", "command": "sed -n '1,109p' hearth/components/interior.py; cat test/test_extension_geometry.py", "exit_code": 0, "seconds": 0, "output_tail": "'/b', 'south')\n    s.place('link', Link(a, b, '/land', curve=curve, covered=True), bindings=(a.binding(), b.binding()))\n    s.finalize()\n    # A six-sided asymmetric footprint absent from the shipped clients.\n    polygon = Polygon(((2, 0), (18, 0), (22, 4), (22, 16), (0, 16), (0, 3)))\n    other = Scene(19, domain=Box((-10, -4, -10), (40, 60, 40)))\n    other.place('land', Terrain(other.domain, gradient(.01, .02, 3)))\n    other.place('hall', Pavilion(polygon, '/land', entrances=('north', 'south'), height=7, roof_run=3))\n    other.finalize()\n\n\ndef test_graph_parent_substitutes_public_access_components():\n    from hearth_extensions.graphs import Campus, Vertex, Edge\n    from hearth_extensions import Pavilion, Polygon\n    domain = Box((-12, -4, -12), (55, 65, 60))\n    variants = []\n    for component in (Platform('/land', 13, 13), Pavilion(Polygon.chamfered(13, 13, 2), '/land', entrances=('north', 'south'))):\n        s = Scene(6, domain=domain)\n        s.place('land', Terrain(domain, gradient(base=3), water_level=3))\n        campus = Campus((Vertex('a', component, Frame()), Vertex('b', Platform('/land'), Frame((25, 0, 29)))), (Edge('a', 'south', 'b', 'north'),), '/land', domain, landscape=False)\n        s.place('group', campus)\n        s.finalize()\n        assert len(s.view.offers('/group', 'access')) == 1\n        assert ('/group/circulation/link-0', 'connects', '/group/a') in s.relations\n        variants.append(semantic_digest(s))\n    assert variants[0] != variants[1]\n\n\n@pytest.mark.parametrize('environment_seed,structure_seed', [(0, 0), (1, 5), (2, 3), (3, 2), (4, 1), (5, 4), (6, 0), (7, 5)])\ndef test_independent_extension_environment_structure_pairs(environment_seed, structure_seed):\n    from hearth_extensions.assessment import freeze_probe, connector_scene\n    frozen = freeze_probe(environment_seed)\n    digest = semantic_digest(frozen)\n    scene = connector_scene(frozen, structure_seed)\n    assert semantic_digest(frozen) == digest\n    assert not validate(scene, True)\n"}
{"timestamp": "2026-09-26T23:10:53.070Z", "command": "python - <<'PY'\nfrom pathlib import Path\nimport shutil,json\nold=Path('reports/extension-corpus/pre-interior-assessment')\nshutil.copytree('reports/extension-corpus/final',old,dirs_exist_ok=True)\nshutil.copy2('reports/extension-corpus/full-regression.txt',old/'full-regression.txt')\nshutil.copy2('reports/extension-corpus/artifact-audit.json',old/'artifact-audit.json')\nmanifest=json.loads(Path('samples/extensions/replay-manifest.json').read_text())\nfor r in manifest:\n p=Path(r['artifact'])\n q=p.parent/'pre-interior'/p.name\n q.parent.mkdir(exist_ok=True)\n shutil.copy2(p,q)\n shutil.copy2(Path(str(p)+'.structure.json'),Path(str(q)+'.structure.json'))\n r['artifact']=str(q)\n(old/'replay-manifest.json').write_text(json.dumps(manifest,indent=2))\nprint('Preserved previous 112 results, tests, audit and 13 artifact pairs.')\nPY", "exit_code": 0, "seconds": 0, "output_tail": "Preserved previous 112 results, tests, audit and 13 artifact pairs.\n"}
{"timestamp": "2026-09-26T23:13:27.392Z", "command": "yapf -i hearth_extensions/interiors.py hearth_extensions/halls.py hearth_extensions/boundaries.py hearth_extensions/assessment.py examples/extension_assess.py && python -m pytest -q test/test_extension_geometry.py -k 'polygon or substitutes' > reports/extension-corpus/interior-first-tests.txt 2>&1", "exit_code": 0, "seconds": 7, "output_tail": ""}
{"timestamp": "2026-09-26T23:13:45.203Z", "command": "cat reports/extension-corpus/interior-first-tests.txt", "exit_code": 0, "seconds": 0, "output_tail": ".....                                                                    [100%]\n5 passed, 14 deselected in 6.70s\n"}
{"timestamp": "2026-09-26T23:14:28.979Z", "command": "yapf -i test/test_extension_interiors.py && python -m pytest -q test/test_extension_interiors.py > reports/extension-corpus/interior-tests.txt 2>&1", "exit_code": 1, "seconds": 3, "output_tail": ""}
{"timestamp": "2026-09-26T23:14:37.240Z", "command": "rg -n 'def clearance' hearth/kernel/view.py; cat reports/extension-corpus/interior-tests.txt; python - <<'PY'\nimport json\nfrom pathlib import Path\nrows=json.loads(Path('reports/extension-corpus/final/results.json').read_text())\nfor r in rows:\n if r['status']!='validated':\n  print(r['label'],r['diagnostic'])\nprint('metrics')\nfor group in ('connector','boundary','hall','campus','perimeter','stress'):\n rr=[r for r in rows if r['label'].startswith(group+'-') and r['status']=='validated']\n print(group, [(k,min(r[k] for r in rr),max(r[k] for r in rr)) for k in ('sitework_occupied_cells','excavated_solid_cells')])\nPY", "exit_code": 0, "seconds": 0, "output_tail": "achable_and_preserve_inventory[kitchen]\nFAILED test/test_extension_interiors.py::test_functional_bays_are_reachable_and_preserve_inventory[bedroom]\nFAILED test/test_extension_interiors.py::test_functional_bays_are_reachable_and_preserve_inventory[living]\nFAILED test/test_extension_interiors.py::test_functional_bays_are_reachable_and_preserve_inventory[storage]\n6 failed, 3 passed in 3.79s\nstress-00 {'rule': 'support-height', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Bounded support depth exceeded'}\nstress-01 {'rule': 'site-relief', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Measured 7, permitted 5'}\nstress-04 {'rule': 'support-height', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Bounded support depth exceeded'}\nstress-07 {'rule': 'site-relief', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Measured 6, permitted 5'}\nstress-09 {'rule': 'support-height', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Bounded support depth exceeded'}\nstress-10 {'rule': 'support-height', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Bounded support depth exceeded'}\nstress-18 {'rule': 'site-relief', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Measured 6, permitted 5'}\nstress-19 {'rule': 'support-height', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Bounded support depth exceeded'}\nstress-22 {'rule': 'support-height', 'component': '/a', 'position': [0, 0, 0], 'conditions': 'Bounded support depth exceeded'}\nmetrics\nconnector [('sitework_occupied_cells', 294, 445), ('excavated_solid_cells', 0, 0)]\nboundary [('sitework_occupied_cells', 168, 224), ('excavated_solid_cells', 0, 0)]\nhall [('sitework_occupied_cells', 38, 71), ('excavated_solid_cells', 0, 0)]\ncampus [('sitework_occupied_cells', 1374, 1593), ('excavated_solid_cells', 0, 0)]\nperimeter [('sitework_occupied_cells', 1539, 1597), ('excavated_solid_cells', 0, 0)]\nstress [('sitework_occupied_cells', 402, 599), ('excavated_solid_cells', 0, 0)]\n"}
{"timestamp": "2026-09-26T23:14:44.769Z", "command": "rg -n '^    def ' hearth/kernel/view.py", "exit_code": 0, "seconds": 0, "output_tail": "24:    def __init__(self):\n29:    def point(self, p):\n38:    def region(self, box):\n43:    def bounds(self):\n49:    def __init__(self, scene, trace=None):\n54:    def version(self):\n58:    def domain(self):\n62:    def style(self):\n65:    def preference(self, name, point):\n71:    def state(self, p):\n80:    def owner(self, p):\n86:    def supports(self, p):\n89:    def clear(self, box):\n92:    def elevation(self, x, z, maximum=None):\n100:    def water(self, x, z):\n108:    def sample(self, x, z):\n118:    def port(self, host, key):\n127:    def cost(self, path, tag=None):\n133:    def offers(self, host, kind=None):\n136:    def capability(self, host):\n139:    def resolved_port(self, host, key):\n151:    def contract(self, path):\n156:    def protected(self, box):\n161:    def obstacle_distance(self, p, radius=8):\n"}
{"timestamp": "2026-09-26T23:15:06.880Z", "command": "yapf -i test/test_extension_interiors.py hearth_extensions/__init__.py && python -m pytest -q test/test_extension_interiors.py > reports/extension-corpus/interior-tests-final.txt 2>&1", "exit_code": 0, "seconds": 9, "output_tail": ""}
{"timestamp": "2026-09-26T23:15:39.412Z", "command": "cat reports/extension-corpus/interior-tests-final.txt", "exit_code": 0, "seconds": 0, "output_tail": ".........                                                                [100%]\n9 passed in 9.03s\n"}
{"timestamp": "2026-09-26T23:17:23.427Z", "command": "yapf -i hearth_extensions/assessment.py && python -m examples.extension_assess --seed 0 --output reports/extension-corpus/final --artifacts > reports/extension-corpus/final-assessment.log 2>&1", "exit_code": 0, "seconds": 105, "output_tail": ""}
{"timestamp": "2026-09-26T23:17:44.476Z", "command": "tail -8 reports/extension-corpus/full-regression.txt; tail -10 reports/extension-corpus/final-assessment.log", "exit_code": 0, "seconds": 0, "output_tail": "........................................................................ [ 40%]\n........................................................................ [ 80%]\n......................      62,\n      259\n    ],\n    \"contact_replacement_range\": [\n      0,\n      254\n    ],\n    \"seconds\": 5.065\n  }\n}\n"}
{"timestamp": "2026-09-26T23:17:55.616Z", "command": "python -m pytest -q test > reports/extension-corpus/full-regression.txt 2>&1", "exit_code": 0, "seconds": 128, "output_tail": ""}
{"timestamp": "2026-09-26T23:18:19.896Z", "command": "python - <<'PY'\nfrom pathlib import Path\nimport shutil\nroot=Path('previews/extensions')\nfor name in ('campus-0','perimeter-3','hall-4'):\n dest=root/'pre-interior'/name\n dest.parent.mkdir(exist_ok=True)\n shutil.move(str(root/name),str(dest))\nshutil.copy2('reports/extension-corpus/artifact-audit.json','reports/extension-corpus/original-cli-and-pre-interior-audit.json')\nPY", "exit_code": 0, "seconds": 0, "output_tail": ""}
{"timestamp": "2026-09-26T23:18:40.088Z", "command": "python -m examples.extension_views --seed 0 --input samples/extensions/campus-0.litematic --output previews/extensions/campus-0 --sections > reports/extension-corpus/render-campus-0.txt 2>&1", "exit_code": 0, "seconds": 19, "output_tail": ""}
{"timestamp": "2026-09-26T23:18:41.564Z", "command": "python -m examples.extension_views --seed 3 --input samples/extensions/perimeter-3.litematic --output previews/extensions/perimeter-3 --sections > reports/extension-corpus/render-perimeter-3.txt 2>&1", "exit_code": 0, "seconds": 21, "output_tail": ""}
{"timestamp": "2026-09-26T23:19:17.781Z", "command": "python -m examples.extension_audit --seed 0 --output reports/extension-corpus/artifact-audit.json > reports/extension-corpus/artifact-audit-final.log 2>&1 && python -m examples.extension_replay --seed 3 --environment-seed 7 --output reports/extension-corpus/replay.json > reports/extension-corpus/replay.log 2>&1 && python -m examples.extension_inspection --seed 0 --input samples/extensions/campus-0.litematic --output samples/extensions/inspection.litematic > reports/extension-corpus/inspection.json", "exit_code": 0, "seconds": 57, "output_tail": ""}
{"timestamp": "2026-09-26T23:21:23.552Z", "command": "cat reports/extension-corpus/full-regression.txt reports/extension-corpus/final/summary.json; python - <<'PY'\nimport json\nfrom pathlib import Path\nrows=json.loads(Path('reports/extension-corpus/final/results.json').read_text())\nprint('omitted',sum(r.get('omitted_optional_furnishing_bays',0) for r in rows))\nfor group in ('hall','campus','perimeter'):\n subset=[r for r in rows if r['label'].startswith(group+'-') and r['status']=='validated']\n print(group,'bays range',min(r['furnishing_bays'] for r in subset),max(r['furnishing_bays'] for r in subset))\nprint('audit',Path('reports/extension-corpus/artifact-audit-final.log').read_text()[-1600:])\nPY", "exit_code": 0, "seconds": 0, "output_tail": "  \"contact_replacement_range\": [\n      0,\n      100\n    ],\n    \"seconds\": 13.342\n  },\n  \"boundary\": {\n    \"inputs\": 16,\n    \"outcomes\": {\n      \"validated\": 16\n    },\n    \"candidate_attempts\": 16,\n    \"accepted_candidates\": 16,\n    \"retries\": 0,\n    \"principal_signatures\": 4,\n    \"contact_signatures\": 16,\n    \"contact_cells_range\": [\n      0,\n      0\n    ],\n    \"contact_replacement_range\": [\n      0,\n      0\n    ],\n    \"seconds\": 2.763\n  },\n  \"hall\": {\n    \"inputs\": 12,\n    \"outcomes\": {\n      \"validated\": 12\n    },\n    \"candidate_attempts\": 12,\n    \"accepted_candidates\": 12,\n    \"retries\": 0,\n    \"principal_signatures\": 12,\n    \"contact_signatures\": 12,\n    \"contact_cells_range\": [\n      38,\n      71\n    ],\n    \"contact_replacement_range\": [\n      6,\n      9\n    ],\n    \"seconds\": 8.403\n  },\n  \"campus\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      132,\n      206\n    ],\n    \"contact_replacement_range\": [\n      232,\n      318\n    ],\n    \"seconds\": 25.967\n  },\n  \"perimeter\": {\n    \"inputs\": 6,\n    \"outcomes\": {\n      \"validated\": 6\n    },\n    \"candidate_attempts\": 6,\n    \"accepted_candidates\": 6,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 6,\n    \"contact_cells_range\": [\n      82,\n      129\n    ],\n    \"contact_replacement_range\": [\n      256,\n      451\n    ],\n    \"seconds\": 24.688\n  },\n  \"stress\": {\n    \"inputs\": 24,\n    \"outcomes\": {\n      \"incompatible\": 9,\n      \"validated\": 15\n    },\n    \"candidate_attempts\": 24,\n    \"accepted_candidates\": 15,\n    \"retries\": 0,\n    \"principal_signatures\": 6,\n    \"contact_signatures\": 15,\n    \"contact_cells_range\": [\n      62,\n      259\n    ],\n    \"contact_replacement_range\": [\n      0,\n      254\n    ],\n    \"seconds\": 5.065\n  }\n}omitted 0\nhall bays range 2 8\ncampus bays range 8 12\nperimeter bays range 9 11\naudit \n"}
{"timestamp": "2026-09-26T23:21:45.543Z", "command": "python -m examples.extension_views --seed 4 --input samples/extensions/hall-4.litematic --output previews/extensions/hall-4 --sections > reports/extension-corpus/render-hall-4.txt 2>&1", "exit_code": 0, "seconds": 7, "output_tail": ""}
{"timestamp": "2026-09-26T23:22:05.532Z", "command": "cat /opt/tools/cutaway.py | head -100; sed -n '1,75p' examples/preview.py; python - <<'PY'\nimport json\nfrom pathlib import Path\nfor p in ['reports/extension-corpus/artifact-audit.json','reports/extension-corpus/replay.json']:\n data=json.loads(Path(p).read_text())\n print(p,data.get('verified'),len(data.get('artifacts',[])),[(r['result']['nodes'],r['result']['digest']) for r in data.get('runs',[])])\nPY", "exit_code": 0, "seconds": 0, "output_tail": "or'] + building.frame.origin[1]\n        offset = json.loads(companion_path(source).read_text())['offset']\n        cuts = [('floor-cut', 'y', floor + 4 - offset[1], 210, 64)]\n        stair = next((n for n in scene.nodes.values() if n.type == 'circulation.stair'), None)\n        if stair is not None:\n            occupied = scene.cells(stair.path)\n            # Cut beside the actual flight, including flights in a remote wing.\n            axis = min((0, 2), key=lambda i: max(p[i] for p in occupied) - min(p[i] for p in occupied))\n            limit = max(p[axis] for p in occupied) + 1 - offset[axis]\n            cuts.append(('stair-cut', 'xz'[axis // 2], limit, 110 if axis == 0 else 20, 26))\n        for label, axis, limit, azimuth, elevation in cuts:\n            cut = output / (label + '.litematic')\n            subprocess.run([sys.executable, str(CUTAWAY), '--input', str(source), '--output', str(cut), '--axis', axis, '--max', str(limit), '--name', label], check=True)\n            target = output / (label + '.png')\n            render(cut, target, azimuth, elevation, size, 3.2)\n            records.append({'source': str(source), 'seed': seed, 'label': label, 'axis': axis, 'export_local_cut': limit, 'image': str(target), 'geometry_preview_only': True})\n    (output / 'views.json').write_text(json.dumps(records, indent=2))\n\n\nif __name__ == '__main__':\n    p = argparse.ArgumentParser()\n    p.add_argument('--seed', type=int, default=0)\n    p.add_argument('--input', type=Path)\n    p.add_argument('--output', type=Path, default=Path('previews/default'))\n    p.add_argument('--size', type=int, default=1200)\n    p.add_argument('--cutaways', action='store_true')\n    a = p.parse_args()\n    preview(a.seed, a.input, a.output, a.size, a.cutaways)\nreports/extension-corpus/artifact-audit.json True 13 []\nreports/extension-corpus/replay.json True 0 [(395, 'd1fcbf2f4a2d54192a35864cdb4d03d822d9e4e7cb1dbf44edf2d769431e699a'), (395, 'd1fcbf2f4a2d54192a35864cdb4d03d822d9e4e7cb1dbf44edf2d769431e699a')]\n"}
{"timestamp": "2026-09-26T23:23:08.646Z", "command": "yapf -i examples/extension_views.py && python -m examples.extension_views --seed 4 --input samples/extensions/hall-4.litematic --output previews/extensions/hall-detail --sections --focus /hall > reports/extension-corpus/render-hall-detail.txt 2>&1", "exit_code": 0, "seconds": 8, "output_tail": ""}
{"timestamp": "2026-09-26T23:23:31.094Z", "command": "python - <<'PY'\nfrom pathlib import Path\nimport json\ns=json.loads(Path('reports/extension-corpus/final/results.json').read_text())\nfor group in ('connector','boundary','hall','campus','perimeter','stress'):\n rows=[r for r in s if r['label'].startswith(group+'-') and r['status']=='validated']\n print(group,'sitework',min(r['sitework_occupied_cells'] for r in rows),max(r['sitework_occupied_cells'] for r in rows),'seconds',sum(r['seconds'] for r in rows))\na=json.loads(Path('reports/extension-corpus/artifact-audit.json').read_text())\nprint('audit',len(a['artifacts']),sum(r['component_queries'] for r in a['artifacts']),sum(r['inventories'] for r in a['artifacts']))\nprint('counts',[ (r['label'],r['nodes'],r['blocks'],r['inventories']) for r in a['artifacts']])\nPY", "exit_code": 0, "seconds": 0, "output_tail": "connector sitework 294 445 seconds 13.3416\nboundary sitework 168 224 seconds 2.7629\nhall sitework 38 71 seconds 8.403\ncampus sitework 1374 1593 seconds 25.9673\nperimeter sitework 1539 1597 seconds 24.6875\nstress sitework 402 599 seconds 4.7278\naudit 13 1745 51\ncounts [('connector-0-0', 11, 19794, 0), ('connector-1-2', 11, 21330, 0), ('connector-4-3', 11, 18150, 0), ('connector-7-5', 11, 23835, 0), ('boundary-0-1', 12, 19631, 0), ('boundary-3-3', 12, 21656, 0), ('hall-0', 25, 26881, 2), ('hall-4', 31, 27184, 4), ('hall-7', 30, 27033, 3), ('campus-0', 446, 119395, 10), ('campus-3', 481, 140804, 12), ('perimeter-0', 381, 118811, 10), ('perimeter-3', 401, 139429, 10)]\n"}
{"timestamp": "2026-09-26T23:25:41.170Z", "command": "rg -n 'FAILED|FAILED:|Error|ContractError|write-authority|effect-envelope|child-envelope|boundary-domain|reachability|Traceback' reports/extension-corpus/*tests*.txt reports/extension-corpus/*probes*.txt reports/extensions-baseline/failing*.txt", "exit_code": 0, "seconds": 0, "output_tail": "undary_interfaces_and_external_attachment\nreports/extension-corpus/geometry-probes-2.txt:27:>           raise ValidationError(errors)\nreports/extension-corpus/geometry-probes-2.txt:28:E           hearth.kernel.errors.ValidationError: clearance: /hall/approach-north at (7, 7, -1): Protected player headroom is obstructed\nreports/extension-corpus/geometry-probes-2.txt:33:hearth/kernel/scene.py:391: ValidationError\nreports/extension-corpus/geometry-probes-2.txt:58:>           raise ValidationError(errors)\nreports/extension-corpus/geometry-probes-2.txt:59:E           hearth.kernel.errors.ValidationError: clearance: /hall/approach-north at (9, 7, -1): Protected player headroom is obstructed\nreports/extension-corpus/geometry-probes-2.txt:64:hearth/kernel/scene.py:391: ValidationError\nreports/extension-corpus/geometry-probes-2.txt:89:>           raise ValidationError(errors)\nreports/extension-corpus/geometry-probes-2.txt:90:E           hearth.kernel.errors.ValidationError: clearance: /hall/approach-north at (13, 7, -1): Protected player headroom is obstructed\nreports/extension-corpus/geometry-probes-2.txt:93:hearth/kernel/scene.py:391: ValidationError\nreports/extension-corpus/geometry-probes-2.txt:95:FAILED test/test_extension_geometry.py::test_polygon_hall_multiple_usable_ports[13-17-0-False]\nreports/extension-corpus/geometry-probes-2.txt:96:FAILED test/test_extension_geometry.py::test_polygon_hall_multiple_usable_ports[17-21-3-True]\nreports/extension-corpus/geometry-probes-2.txt:97:FAILED test/test_extension_geometry.py::test_polygon_hall_multiple_usable_ports[25-13-4-False]\nreports/extensions-baseline/failing-replay-probe.txt:11:E       TypeError: Scene.regenerate() got an unexpected keyword argument 'replacements'\nreports/extensions-baseline/failing-replay-probe.txt:13:test/test_extension_protocol.py:59: TypeError\nreports/extensions-baseline/failing-replay-probe.txt:15:FAILED test/test_extension_protocol.py::test_atomic_multi_branch_replay_accepts_explicit_replacements\n"}
{"timestamp": "2026-09-26T23:28:45.348Z", "command": "python - <<'PY'\nfrom hearth import Scene, Box, Frame\nfrom hearth.environment import Terrain, gradient, radial\nfrom hearth_extensions import Polygon, Pavilion, Endpoint, Link\nfrom hearth.persistence import semantic_digest\nfrom pathlib import Path\nimport json\n# Execute the new public example literally without transport.\ndomain = Box((-12,-4,-12),(60,65,65))\nenvironment = Scene(7,domain=domain)\nfield = gradient(.012,-.008,3)+radial(19,22,14,-2)\nenvironment.place('land',Terrain(domain,field,water_level=3))\nenvironment.finalize()\nscene = environment.fork(seed=23)\nshape = Polygon.chamfered(13,13,2)\nscene.place('reading',Pavilion(shape,'/land','library'))\nscene.place('workshop',Pavilion(shape,'/land','workshop'),frame=Frame((25,0,29)))\na,b=Endpoint('/reading','south'),Endpoint('/workshop','north')\nscene.place('gallery',Link(a,b,'/land',covered=True),bindings=(a.binding(),b.binding()))\nscene.finalize()\nPath('reports/extension-corpus/documented-example.json').write_text(json.dumps({'seed':23,'environment_seed':7,'validated':True,'blocks':len(scene.blocks),'nodes':len(scene.nodes),'digest':semantic_digest(scene)},indent=2))\nprint('Documented composition validated',len(scene.blocks),'blocks')\nPY", "exit_code": 0, "seconds": 0, "output_tail": "Documented composition validated 47619 blocks\n"}
{"timestamp": "2026-09-26T23:29:58.334Z", "command": "python - <<'PY'\nfrom pathlib import Path\nimport hashlib,json,re\nbaseline=json.loads(Path('reports/extensions-baseline/module-hashes.json').read_text())['sha256']\ncurrent={p:hashlib.sha256(Path(p).read_bytes()).hexdigest() for p in baseline}\nchanged={p:{'before':baseline[p],'after':current[p]} for p in baseline if baseline[p]!=current[p]}\nallowed={'ARCHITECTURE.md','GENERATOR.md','hearth/__init__.py','hearth/kernel/scene.py','pyproject.toml'}\nassert set(changed)==allowed,changed\nnewpaths=sorted([*Path('hearth_extensions').glob('*.py'),*Path('examples').glob('extension_*.py'),*Path('test').glob('test_extension_*.py')])\nnew={str(p):hashlib.sha256(p.read_bytes()).hexdigest() for p in newpaths}\nreport={'library_version':'1.2.0','baseline_files':len(baseline),'unchanged_files':len(baseline)-len(changed),'changed':changed,'current_original_sha256':current,'new_source_sha256':new,'original_tests_unchanged':all(current[p]==baseline[p] for p in baseline if p.startswith('test/')),'original_default_cli_unchanged':current['generate.py']==baseline['generate.py'],'kernel_modules_changed':['hearth/kernel/scene.py'],'companion_format_changed':False}\nPath('reports/extension-corpus/api-final.json').write_text(json.dumps(report,indent=2))\nprint('Baseline audit:',len(baseline),'files;',len(changed),'expected changes;',len(new),'new source files')\nfor p in changed: print(p)\n# Check local documentation links in the updated docs only.\nmissing=[]\nfor p in [Path('ARCHITECTURE.md'),Path('GENERATOR.md'),Path('hearth_extensions/README.md'),Path('reports/extension-corpus/ASSESSMENT.md'),Path('samples/extensions/README.md'),Path('previews/extensions/README.md')]:\n for ref in re.findall(r'\\]\\(([^)]+)\\)',p.read_text()):\n  if '://' in ref or ref.startswith('#'): continue\n  target=ref.split('#')[0]\n  if not (p.parent/target).exists(): missing.append([str(p),target])\nprint('Missing local links:',missing)\nassert not missing\nfamilies=('campus-0','perimeter-3','wet-connector','curve-boundary','hall-4','hall-detail')\ncounts={f:len(list((Path('previews/extensions')/f).glob('*.png'))) for f in families}\nprint('Final render counts:',counts,'total',sum(counts.values()))\nPY", "exit_code": 0, "seconds": 0, "output_tail": "Baseline audit: 45 files; 5 expected changes; 21 new source files\nARCHITECTURE.md\nGENERATOR.md\nhearth/__init__.py\nhearth/kernel/scene.py\npyproject.toml\nMissing local links: []\nFinal render counts: {'campus-0': 5, 'perimeter-3': 5, 'wet-connector': 5, 'curve-boundary': 3, 'hall-4': 4, 'hall-detail': 4} total 26\n"}
{"timestamp": "2026-09-26T23:29:58.363Z", "command": "node -e \"try { console.log(require.resolve('pyodide')) } catch (error) { console.log('Pyodide is not installed in the local Node module search path') }\"; rg -n '_scene|ctx\\._|\\._state|scene\\.blocks\\[.*=' hearth_extensions examples/extension_*.py", "exit_code": 0, "seconds": 0, "output_tail": ",int(sys.argv[2]))\nexamples/extension_replay.py:20:output=Path(sys.argv[3]);export_scene(scene,output)\nexamples/extension_replay.py:21:restored=load_scene(output)\nexamples/extension_generate.py:4:from hearth.persistence import export_scene, semantic_digest\nexamples/extension_generate.py:5:from hearth_extensions.programs import extension_scene, branching_campus, perimeter_cluster\nexamples/extension_generate.py:17:    scene = extension_scene(PROGRAMS[args.program], args.seed, args.environment_seed)\nexamples/extension_generate.py:18:    export_scene(scene, args.output)\nhearth_extensions/assessment.py:12:from hearth.persistence import semantic_digest, export_scene\nhearth_extensions/assessment.py:37:def connector_scene(environment, structure_seed):\nhearth_extensions/assessment.py:48:def boundary_scene(environment, structure_seed):\nhearth_extensions/assessment.py:58:def hall_scene(seed, environment_seed=None):\nhearth_extensions/assessment.py:135:            export_scene(scene, artifact)\nhearth_extensions/assessment.py:166:            record(f'connector-{i}-{bs}', lambda e=frozen[es], b=bs: connector_scene(e, b), {'environment': inputs[i], 'structure_seed': bs}, (i, bs) in ((0, 0), (1, 2), (4, 3), (7, 5)))\nhearth_extensions/assessment.py:175:            record(f'boundary-{es}-{bs}', lambda e=env, b=bs: boundary_scene(e, b), {'environment_seed': es + root_seed * 100, 'expression': field.expression(), 'structure_seed': bs}, (es, bs) in ((0, 1), (3, 3)))\nhearth_extensions/assessment.py:177:        record(f'hall-{seed}', lambda b=seed: hall_scene(b, root_seed * 100 + b), {'structure_seed': seed, 'environment_seed': root_seed * 100 + seed}, seed in (0, 4, 7))\nhearth_extensions/assessment.py:198:        record(f'stress-{i:02}', lambda e=env, b=i % 6: connector_scene(e, b), {'environment_seed': es, 'structure_seed': i % 6, 'expression': field.expression(), 'water': water})\nhearth_extensions/programs.py:52:def extension_scene(program=branching_campus, seed=0, environment_seed=0):\n"}
{"timestamp": "2026-09-26T23:31:00.773Z", "command": "python - <<'PY'\nfrom pathlib import Path\nimport json,shutil,hashlib\n# Remove generated bytecode only from our runtime/test directories, never references.\nfor folder in ('hearth','hearth_extensions','extension_demo','examples','test'):\n for path in Path(folder).rglob('__pycache__'):\n  shutil.rmtree(path)\nleft=[str(p) for p in Path('test').iterdir() if p.is_dir() and p.name.startswith('tmp')]\nassert not left,left\n# Final source hashes should still describe the actual delivered implementation.\na=json.loads(Path('reports/extension-corpus/api-final.json').read_text())\nfor p,digest in {**a['current_original_sha256'],**a['new_source_sha256']}.items():\n assert hashlib.sha256(Path(p).read_bytes()).hexdigest()==digest,p\nr=json.loads(Path('reports/extension-corpus/artifact-audit.json').read_text())\nassert r['verified'] and len(r['artifacts'])==13\nstats=json.loads(Path('reports/extension-corpus/final/summary.json').read_text())\nassert sum(v['inputs'] for v in stats.values())==112\nassert sum(v['accepted_candidates'] for v in stats.values())==103\nprint('Final sources match audited hashes; test temporary directories cleaned; 13 pairs and 112 input records retained.')\nPY", "exit_code": 0, "seconds": 0, "output_tail": "Final sources match audited hashes; test temporary directories cleaned; 13 pairs and 112 input records retained.\n"}
{"timestamp": "2026-09-26T23:31:27.809Z", "command": "python - <<'PY'\nimport json\nfrom pathlib import Path\nr=json.loads(Path('reports/extension-corpus/inspection.json').read_text())\nprint([(q['tag'],q['query']['state'],q['query']['instance']) for q in r['queries']])\nPY", "exit_code": 0, "seconds": 0, "output_tail": "[('deck', 'minecraft:spruce_planks', '/campus/circulation/link-0/deck'), ('window', 'minecraft:glass', '/campus/hall/facade/window-east-0'), ('fixture', 'minecraft:lantern[hanging=false,waterlogged=false]', '/campus/boundary-lights/segment-0-mount-16')]\n"}
