Files
Emil 89c53d7058 Expand material support and checkpoint the completed building toolkit
Expose the runtime block/item registry through compact material search and
single-material descriptions. Preserve private block-entity data through
checked edits and durable undo without sending payloads to model context.

Include the completed terrain tools, isolated world/map plugin, station lift,
ACP streaming and guidance fixes, local camera auto-connect, construction
scripts, and their public documentation, references and verification records.
Active station decoration and private runtime data remain outside this commit.

Validation: 145 Maven tests, 47 Bridge tests, successful camera Gradle build,
and isolated Paper verification of all 1,196 block defaults plus 5,392
independent property cases for placement, same-material edits and restoration.
2026-09-13 16:59:13 +03:00

290 lines
16 KiB
Python

#!/usr/bin/env python3
"""Read-only candidate and observed-volume QA for the arrival-square balustrade.
Checks stable capped-wall states, cardinal continuity, grounded footings, original
road masks, preserved garden fixtures and remaining walkable floor. Actual mode
also compares every captured voxel against baseline plus the desired overlay.
"""
import argparse
from collections import Counter, deque
import hashlib
import importlib.util
import json
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location('balustrade_plaza_qa', ROOT / 'scripts/verify-plaza.py')
plaza = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(plaza)
survey = plaza.survey
N = {'east': (1, 0), 'north': (0, -1), 'south': (0, 1), 'west': (-1, 0)}
CAP = 'minecraft:smooth_sandstone_slab[type=bottom,waterlogged=false]'
def coordinates(document):
result = {}
for row in document['blocks']:
at = tuple(survey.point(row)[axis] for axis in ('x', 'y', 'z'))
if at in result:
raise ValueError(f'Duplicate desired position {at}')
result[at] = row['block']
return result
def stable_wall_properties(connected, capped=True):
"""Vanilla 26.2 WallBlock under a full bottom face (the bottom slab cap).
Cap coverage makes arms tall. shouldRaisePost suppresses the post for either
opposite tall pair only after its endpoint/corner/T asymmetry condition.
"""
ns = connected['north'] != connected['south']
ew = connected['east'] != connected['west']
opposite = ((connected['north'] and connected['south']) or
(connected['east'] and connected['west']))
up = ns or ew or not opposite
return {**{name: ('tall' if capped else 'low') if connected[name] else 'none' for name in N},
'up': str(up).lower(), 'waterlogged': 'false'}
def wall_neighbor(state):
name, _ = plaza.parse(state)
return name.endswith('_wall') or name in plaza.STATIC_CUBES
def check_guard(reader, metadata):
columns = set(map(tuple, metadata['fence_columns']))
piers = set(map(tuple, metadata['pier_columns']))
failures, listed = [], []
if not columns or not piers <= columns:
raise ValueError('Fence columns and piers must form a nonempty valid set')
for index, raw in enumerate(metadata['path_runs']):
run = list(map(tuple, raw))
if len(run) < 2 or run[0] not in piers or run[-1] not in piers:
failures.append({'reason': 'run_needs_pier_endpoints', 'run': index})
for a, b in zip(run, run[1:]):
if abs(a[0] - b[0]) + abs(a[1] - b[1]) != 1:
failures.append({'reason': 'noncardinal_guard_gap', 'from': a, 'to': b})
listed.extend(run)
if len(listed) != len(set(listed)) or set(listed) != columns:
failures.append({'reason': 'path_runs_do_not_cover_fence_once'})
for x, z in sorted(columns):
state = reader.state(x, 96, z)
material, props = plaza.parse(state)
if (x, z) in piers:
if material != 'cut_sandstone':
failures.append({'at': [x, 96, z], 'reason': 'pier_material', 'actual': state})
else:
connections = {name: wall_neighbor(reader.state(x + dx, 96, z + dz))
for name, (dx, dz) in N.items()}
expected = stable_wall_properties(connections)
if material != 'stone_brick_wall' or props != expected:
failures.append({'at': [x, 96, z], 'reason': 'unstable_wall_state',
'actual': state, 'expected_properties': expected})
if reader.state(x, 97, z) != CAP:
failures.append({'at': [x, 97, z], 'reason': 'missing_continuous_bottom_cap'})
if plaza.parse(reader.state(x, 95, z))[0] not in plaza.STATIC_CUBES:
failures.append({'at': [x, 95, z], 'reason': 'guard_without_full_base'})
for footing in metadata['footing_columns']:
x, z, bottom = footing['x'], footing['z'], footing['support_y']
if (x, z) not in columns or not 70 <= bottom < 95:
raise ValueError('Unexpected footing coordinate or support height')
if plaza.parse(reader.state(x, bottom, z))[0] == 'grass_block':
failures.append({'at': [x, bottom, z], 'reason': 'grass_under_opaque_footing_will_decay_to_dirt'})
for y in range(bottom, 96):
if plaza.parse(reader.state(x, y, z))[0] not in plaza.STATIC_CUBES:
failures.append({'at': [x, y, z], 'reason': 'footing_gap_or_unknown_support'})
# A newly connected pier also changes the reciprocal side of an old road
# rail. Inspect real neighbors, including walls absent from compiler masks.
adjacent = {(x + dx, z + dz) for x, z in columns for dx, dz in N.values()
if (x + dx, z + dz) not in columns and
plaza.parse(reader.state(x + dx, 96, z + dz))[0] == 'stone_brick_wall'}
for x, z in sorted(adjacent):
above = reader.state(x, 97, z)
if above not in survey.AIR and above != CAP:
failures.append({'at': [x, 97, z], 'reason': 'unknown_neighbor_rail_top_shape', 'actual': above})
continue
connected = {name: wall_neighbor(reader.state(x + dx, 96, z + dz))
for name, (dx, dz) in N.items()}
actual = reader.state(x, 96, z)
expected = stable_wall_properties(connected, above == CAP)
if plaza.parse(actual)[1] != expected:
failures.append({'at': [x, 96, z], 'reason': 'unstable_reciprocal_road_rail',
'actual': actual, 'expected_properties': expected})
pending, components = set(columns), []
while pending:
seen, queue = set(), deque([next(iter(pending))])
while queue:
p = queue.popleft()
if p not in pending:
continue
pending.remove(p)
seen.add(p)
queue.extend((p[0] + dx, p[1] + dz) for dx, dz in N.values())
components.append(len(seen))
if sorted(components) != sorted(len(run) for run in metadata['path_runs']):
failures.append({'reason': 'actual_cardinal_components_differ_from_runs', 'components': components})
return {'passed': not failures, 'columns': len(columns), 'piers': len(piers),
'cardinal_components': sorted(components), 'grounded_footings': len(metadata['footing_columns']),
'adjacent_road_rails': len(adjacent),
'failures': failures}
def captured_voxels(snapshot):
for cell in snapshot.cells.values():
lo, hi, index = cell['min'], cell['max'], 0
for y in range(lo['y'], hi['y'] + 1):
for z in range(lo['z'], hi['z'] + 1):
for x in range(lo['x'], hi['x'] + 1):
yield (x, y, z), cell['palette'][cell['indices'][index]]
index += 1
def compare_volume(before, after, desired):
mismatches, counts = [], Counter()
for at, baseline in captured_voxels(before):
changed = at in desired
counts['desired_voxels' if changed else 'outside_desired_voxels'] += 1
actual, expected = after.state(*at), desired.get(at, baseline)
if actual != expected:
counts['desired_mismatches' if changed else 'outside_desired_mismatches'] += 1
if len(mismatches) < 30:
mismatches.append({'at': list(at), 'expected': expected, 'actual': actual,
'inside_desired': changed})
if counts['desired_voxels'] != len(desired):
raise ValueError('Desired blocks extend outside the captured baseline volume')
return {'passed': not mismatches, **dict(counts), 'mismatch_examples': mismatches,
'scope_note': 'Every voxel inside the supplied before snapshot; no claim for unobserved exterior voxels.'}
def audit(before, layout, metadata, garden_plan, garden_metadata, garden_walk, nav, after=None):
scope = survey.scope_of(layout['scope'])
if any(value != scope for value in (before.scope, metadata['scope'], garden_plan['scope'],
garden_metadata['scope'], garden_walk['scope'])) or (after and after.scope != scope):
raise ValueError('Project/world/epoch differs between inputs')
desired, failures = coordinates(layout), []
for row in layout['blocks']:
at = tuple(row[a] for a in ('x', 'y', 'z'))
if before.state(*at) != row['expected']:
raise ValueError(f'Stale expected block at {at}')
fence = set(map(tuple, metadata['fence_columns']))
road = {tuple(p) for route in nav['routes'] for p in route['clear_cells']}
bench = set(map(tuple, garden_metadata['bench_access_columns']))
for x, y, z in desired:
if (x, z) in road or (x, z) in bench:
failures.append({'at': [x, y, z], 'reason': 'protected_road_or_bench_column_edited'})
if fence & (road | bench):
failures.append({'reason': 'fence_declared_on_protected_road_or_bench'})
reader = plaza.Reader(after or before, None if after else desired)
guard = check_guard(reader, metadata)
old_desired = coordinates(garden_plan)
protected = {at: state for at, state in old_desired.items()
if plaza.parse(state)[0] in plaza.FLOWERS | {'spruce_stairs', 'spruce_fence', 'lantern',
'iron_chain', 'iron_bars', 'gold_block'} or any(part in state for part in ('_leaves', '_log', 'waxed_oxidized_cut_copper'))}
for fixture in garden_metadata['fixtures']:
if fixture['type'] in ('conifer', 'topiary'):
at = fixture['x'], 95, fixture['z']
protected[at] = before.state(*at)
lost = [{'at': list(at), 'expected': state, 'actual': reader.state(*at)}
for at, state in protected.items() if reader.state(*at) != state]
fixture_report = plaza.check_fixtures(reader, old_desired, garden_metadata)
blocked = set(map(tuple, garden_metadata['unwalkable_columns'])) | fence
points = [p for p in garden_walk['points'] if (p['x'], p['z']) not in fence]
walking = survey.verify_walkable(reader, points)
navigation = json.loads(json.dumps(nav))
for cell in navigation['cells']:
if (cell['x'], cell['z']) in blocked:
cell['clear'] = False
nav_report = plaza.navigation.audit(navigation)
graph = plaza.navigation.surface_graph(plaza.navigation.normalize_cells(navigation))
reached = plaza.navigation.reachable(graph, (0, 9))
bench_report = [plaza.navigation.endpoint_report(f'bench-access-{x}-{z}', (x, z), graph, reached)
for x, z in sorted(bench)]
nav_report['bench_access'] = bench_report
nav_report['passed'] &= all(row['passed'] for row in bench_report)
volume = compare_volume(before, after, desired) if after else None
passed = (not failures and not lost and guard['passed'] and fixture_report['passed'] and
walking['passed'] and nav_report['passed'] and (volume is None or volume['passed']))
return {'version': 1, 'scope': scope, 'mode': 'actual_after_snapshot' if after else 'candidate_overlay',
'passed': passed, 'world_edits': 0, 'desired_blocks': len(desired), 'guard': guard,
'protected_road_columns': len(road), 'protection_failures': failures,
'preserved_fixture_states': len(protected), 'lost_fixtures': lost[:30],
'fixtures': fixture_report, 'walking': walking, 'navigation': nav_report,
'volume': volume, 'note': 'Point-sampled body headroom and navigation; not a full moving-player collision simulation.'}
class BalustradeTests(unittest.TestCase):
def test_stable_capped_wall_corner_and_straight_and_cross(self):
for sides, up in [({'north', 'south'}, 'false'), ({'east', 'west'}, 'false'),
(set(N), 'false'), ({'north', 'east'}, 'true'),
({'north', 'east', 'south'}, 'true'), ({'north'}, 'true')]:
self.assertEqual(stable_wall_properties({n: n in sides for n in N})['up'], up)
uncapped = stable_wall_properties({n: n in {'east', 'west'} for n in N}, False)
self.assertEqual((uncapped['east'], uncapped['west'], uncapped['up']), ('low', 'low', 'false'))
def test_volume_detects_unrelated_change_and_exact_properties(self):
before = type('Snapshot', (), {'cells': {0: {'min': dict(x=0, y=0, z=0),
'max': dict(x=1, y=0, z=0), 'palette': ['minecraft:air'], 'indices': [0, 0]}}})()
states = {(0, 0, 0): CAP, (1, 0, 0): 'minecraft:stone'}
after = type('After', (), {'state': lambda self, x, y, z: states[x, y, z]})()
result = compare_volume(before, after, {(0, 0, 0): CAP})
self.assertFalse(result['passed'])
self.assertEqual(result['outside_desired_mismatches'], 1)
states[(1, 0, 0)] = 'minecraft:air'
self.assertTrue(compare_volume(before, after, {(0, 0, 0): CAP})['passed'])
states[(0, 0, 0)] = CAP.replace('bottom', 'top')
self.assertEqual(compare_volume(before, after, {(0, 0, 0): CAP})['desired_mismatches'], 1)
def test_diagonal_run_is_rejected(self):
states = {(x, y, z): 'minecraft:air' for x in range(-1, 3) for z in range(-1, 3) for y in range(95, 98)}
for x, z in [(0, 0), (1, 1)]:
states[x, 95, z] = 'minecraft:stone'
states[x, 96, z] = 'minecraft:cut_sandstone'
states[x, 97, z] = CAP
reader = type('Reader', (), {'state': lambda self, x, y, z: states[x, y, z]})()
meta = {'fence_columns': [[0, 0], [1, 1]], 'pier_columns': [[0, 0], [1, 1]],
'path_runs': [[[0, 0], [1, 1]]], 'footing_columns': []}
result = check_guard(reader, meta)
self.assertIn('noncardinal_guard_gap', {f['reason'] for f in result['failures']})
def main():
parser = argparse.ArgumentParser(description=__doc__)
stage, garden = ROOT / '.runtime/balustrade-stage04', ROOT / '.runtime/plaza-stage03'
parser.add_argument('--before', type=Path, default=stage / 'before-full.json.gz')
parser.add_argument('--after', type=Path)
parser.add_argument('--layout', type=Path, default=stage / 'balustrade.json')
parser.add_argument('--metadata', type=Path, default=stage / 'balustrade.metadata.json')
parser.add_argument('--garden-plan', type=Path, default=garden / 'plaza-polished.json')
parser.add_argument('--garden-metadata', type=Path, default=garden / 'plaza-polished.metadata.json')
parser.add_argument('--garden-walk', type=Path, default=garden / 'plaza-polished.walk.json')
parser.add_argument('--navigation', type=Path, default=ROOT / '.runtime/foundations-stage02/navigation-verified-walkable-input.json')
parser.add_argument('--report', type=Path, default=stage / 'candidate-qa.json')
parser.add_argument('--self-test', action='store_true')
args = parser.parse_args()
if args.self_test:
result = unittest.TextTestRunner().run(unittest.defaultTestLoader.loadTestsFromTestCase(BalustradeTests))
raise SystemExit(0 if result.wasSuccessful() else 1)
paths = {key: getattr(args, key) for key in ('layout', 'metadata', 'garden_plan', 'garden_metadata', 'garden_walk', 'navigation')}
documents = [json.loads(path.read_text()) for path in paths.values()]
before = survey.load_snapshot(args.before)
after = survey.load_snapshot(args.after, before.scope) if args.after else None
report = audit(before, *documents, after=after)
paths['before'] = args.before
if args.after:
paths['after'] = args.after
report['input_sha256'] = {key: hashlib.sha256(path.read_bytes()).hexdigest() for key, path in paths.items()}
args.report.parent.mkdir(parents=True, exist_ok=True)
survey.terrain.save(args.report, report)
print(json.dumps({key: report[key] for key in ('passed', 'mode', 'desired_blocks', 'guard', 'protection_failures',
'lost_fixtures', 'fixtures', 'volume')}
| {'walk_points': report['walking']['checked_points'], 'walk_failures': report['walking']['failures'][:10],
'navigation_passed': report['navigation']['passed'],
'unreachable_columns': report['navigation']['unreachable_clear_columns'],
'report': str(args.report.resolve())}, indent=2))
raise SystemExit(0 if report['passed'] else 1)
if __name__ == '__main__':
main()