AnasSarkiz/magnetic-shutter-remote-r8

A USB‑C rechargeable battery-powered ESP32‑C3 wireless controller with Li‑ion charging, protected 3.3‑V buck‑boost regulation, thermal/safety monitoring, status LEDs, buttons, programming UART, crystal clock, and 2.4‑GHz antenna matching network.

Version
0.4.0-prototype
License
unset
Stars
0

scripts/stage-registry.py

"""Stage the selected native circuit and pinned dependencies for tsci push.

The public Git repository retains full qualification/reproduction evidence.
Registry staging includes only the board source closure, models, build JSON,
review documentation and required file dependencies; never caches or SDKs.
"""
import argparse
from dataclasses import dataclass
import hashlib
import json
import shutil
import subprocess
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
QUALIFICATION = ROOT / 'evidence/R8-standard-programmer-2026-10-05'
IMPORTS_BY_PATH = {}


def module_specifiers(path):
    if not IMPORTS_BY_PATH:
        sources = {p.resolve() for p in ROOT.iterdir() if p.suffix in {'.ts','.tsx'}}
        for directory in ['scripts','tests','src','product','imports']:
            sources.update(p.resolve() for p in (ROOT/directory).rglob('*') if p.is_file() and p.suffix in {'.ts','.tsx'})
        read_imports(sources)
    if str(path) not in IMPORTS_BY_PATH:
        read_imports({path})
    return IMPORTS_BY_PATH[str(path)]


def read_imports(paths):
    result = subprocess.run(['bun',str(ROOT/'scripts/list-local-imports.ts')],
                            input=json.dumps(sorted(str(p) for p in paths)),
                            text=True,capture_output=True,check=True,cwd=ROOT)
    IMPORTS_BY_PATH.update(json.loads(result.stdout))


@dataclass(frozen=True)
class StageOptions:
    evidence: Path
    supplemental_files: tuple[Path, ...] = ()
    include_product_assembly: bool = False


def supplemental_files(options, circuit_sha256):
    selected = set()
    for requested in options.supplemental_files:
        path = (ROOT / requested).resolve()
        relative = path.relative_to(ROOT)
        if relative.parts[0] not in {'evidence', 'firmware', 'mechanical', 'tscircuit-issues'}:
            raise ValueError(f'Supplemental input must be board evidence: {relative}')
        if any(part.startswith('.') for part in relative.parts) or not path.is_file():
            raise ValueError(f'Supplemental input must be a visible regular file: {relative}')
        if path.suffix == '.json':
            report = json.loads(path.read_text())
            if isinstance(report, dict):
                for key in ('circuit_sha256', 'circuitSha256'):
                    if key in report and report[key] != circuit_sha256:
                        raise ValueError(f'Supplemental report belongs to another circuit: {relative}')
        selected.add(path)
    return selected


def resolve_import(specifier, parent):
    target = (parent / specifier).resolve()
    candidates = [target, *(Path(str(target) + suffix) for suffix in ['.ts', '.tsx']),
                  target / 'index.ts', target / 'index.tsx']
    matches = [path for path in candidates if path.is_file()]
    if len(matches) != 1:
        raise ValueError(f'Local import must resolve uniquely: {specifier} in {parent}')
    selected = matches[0]
    selected.relative_to(ROOT)
    return selected


def source_closure(entry):
    pending, selected = [entry], set()
    while pending:
        path = pending.pop()
        if path in selected:
            continue
        selected.add(path)
        if path.suffix in ['.ts', '.tsx']:
            specifiers = module_specifiers(path)
            pending.extend(resolve_import(specifier, path.parent) for specifier in specifiers)
    return selected


def verify_native_product(options):
    reviews = []
    browser_reviews = []
    for requested in options.supplemental_files:
        path = (ROOT/requested).resolve()
        path.relative_to(ROOT)
        if path.suffix == '.json':
            report = json.loads(path.read_text())
            if isinstance(report, dict) and 'native_views_checked' in report:
                reviews.append(report)
            if isinstance(report, dict) and report.get('browser_render_review'):
                browser_reviews.append(report)
    if len(reviews) != 1:
        raise ValueError('Product staging requires one independent native GLB review')
    report = reviews[0]
    if not report.get('passes') or set(report['native_views_checked']) != {'product.assembly','product.exploded','enclosure'}:
        raise ValueError('Native product views did not pass independent review')
    for view, sha256 in report['native_glb_sha256'].items():
        if hashlib.sha256((ROOT/'dist'/view/'3d.glb').read_bytes()).hexdigest() != sha256:
            raise ValueError(f'Native product GLB changed after review: {view}')
    mechanical_checks = [row for row in report['actual_glb_geometry_review'] if 'part' in row]
    if len(mechanical_checks) != 30 or any(not row.get('outward_face_winding_preserved') for row in mechanical_checks):
        raise ValueError('All ten mechanical face directions must pass in all three views')
    if len(browser_reviews) != 1:
        raise ValueError('Product staging requires one actual native browser geometry review')
    browser = browser_reviews[0]
    if not browser.get('passes') or browser.get('browser_errors') or browser.get('geometry_checks') != 110:
        raise ValueError('Both browser views must preserve all 55 qualified model poses')
    for path, sha256 in browser['model_sha256'].items():
        if hashlib.sha256((ROOT/path).read_bytes()).hexdigest() != sha256:
            raise ValueError(f'Browser loaded a stale model: {path}')
    for view, sha256 in browser['view_circuit_sha256'].items():
        if hashlib.sha256((ROOT/'dist'/view/'circuit.json').read_bytes()).hexdigest() != sha256:
            raise ValueError(f'Browser loaded a stale product circuit: {view}')


def stage(destination, options):
    evidence = options.evidence.resolve()
    evidence.relative_to(ROOT / 'evidence')
    destination = destination.resolve()
    destination.relative_to(ROOT / '.codex/runtime')
    if destination.exists():
        raise ValueError('Existing staging directory: inspect rather than replace it')
    circuit = json.loads((ROOT / 'dist/index/circuit.json').read_text())
    if any(element['type'].endswith('_error') for element in circuit):
        raise ValueError('Current generated circuit contains errors')
    native = json.loads((evidence / 'final-native-checks.json').read_text())
    if native.get('circuitSha256') != hashlib.sha256((ROOT / 'dist/index/circuit.json').read_bytes()).hexdigest():
        raise ValueError('Native checks belong to a different generated circuit')
    if (native['generatedErrors'] or native['holeTrace'] or native['dangling'] or
            native['selfShorts'] or any(e['type'].endswith('_error') for e in native['all'])):
        raise ValueError('Current native checks did not pass')
    manufacturing = json.loads((evidence / 'final-manufacturing.json').read_text())
    connectivity = json.loads((evidence / 'physical-connectivity.json').read_text())
    circuit_sha256 = hashlib.sha256((ROOT / 'dist/index/circuit.json').read_bytes()).hexdigest()
    for filename in ['final-manufacturing.json', 'physical-connectivity.json',
                     'power-path-measurements.json', 'trace-width-review.json',
                     'electrical-contract.json', 'final-process/process-review.json',
                     'final-readback/readback.json']:
        report = json.loads((evidence / filename).read_text())
        if report.get('circuit_sha256') != circuit_sha256:
            raise ValueError(f'Independent report belongs to a different circuit: {filename}')
        if report.get('failures') or report.get('manufacturing_failures'):
            raise ValueError(f'Independent report did not pass: {filename}')
    if manufacturing['failures'] or any(not net['connected'] for net in connectivity['connectivity']):
        raise ValueError('Independent manufacturing/connectivity checks did not pass')
    if connectivity['rf_copper_intrusions'] or connectivity['shutter_manufacturer_copper_intrusions']:
        raise ValueError('Current copper intrudes into an exclusion')
    if any(not t['continuous_top_ground_plane_covers_ep_and_ground_pin'] for t in connectivity['thermal']):
        raise ValueError('Thermal ground attachment did not pass')
    package = json.loads((ROOT / 'package.json').read_text())
    selected = source_closure(ROOT / 'index.circuit.tsx')
    selected.update(ROOT / name for name in ['package.json', 'bun.lock', 'tsconfig.json',
                    'tscircuit.config.json', 'README.md', 'VALIDATION.md', 'BOM.csv', 'BOM.md',
                    'bom.json', 'dist/index/circuit.json', 'firmware/README.md',
                    'mechanical/R8-MAGSAFE-REQUIREMENTS.md',
                    'mechanical/R8-MAGSAFE-RESEARCH.md',
                    'mechanical/R8-MAGSAFE-REFERENCE-REVIEW.json',
                    'firmware/artifacts/R8-DOIT-C3-hosted-2026-10-05/zephyr.bin',
                    'firmware/artifacts/R8-DOIT-C3-hosted-2026-10-05/BUILD-MANIFEST.json'])
    for section in ['dependencies', 'devDependencies', 'overrides']:
        for specifier in package.get(section, {}).values():
            if specifier.startswith('file:'):
                selected.add(resolve_import(specifier[5:], ROOT))
    for directory in ['scripts', 'tests']:
        for path in (ROOT / directory).rglob('*'):
            if path.is_file() and path.suffix in ['.py', '.ts', '.tsx']:
                selected.update(source_closure(path))
    selected.update(evidence / name for name in ['REVIEW.md', 'final-native-checks.json',
                    'physical-connectivity.json', 'power-path-measurements.json',
                    'SW2-supplier-terminal-registration.json', 'J3-supplier-terminal-registration.json',
                    'final-manufacturing.json', 'final-process/process-review.json',
                    'final-readback/readback.json', 'trace-width-review.json',
                    'electrical-contract.json'])
    selected.update(QUALIFICATION / name for name in [
                    'qualification/QUALIFICATION.md', 'qualification/land-audit.json',
                    'qualification/programmer-release.json', 'qualification/supplier-reference/circuit.json',
                    'qualification/C160389-supplier.json', 'qualification/programmer-0.8.0/circuit.json',
                    'PROGRAMMING.md', 'unrouted/circuit.json'])
    selected.update(ROOT / name for name in [
                    'evidence/USB4215-import-audit/final/circuit.json',
                    'evidence/R8-side-shutter-2026-10-05/qualification/supplier-reference/circuit.json',
                    'fabrication/R8-standard-programmer-2026-10-05/R8-standard-programmer-route04-Gerbers.zip',
                    'tscircuit-issues/091-tangent-pour-cutouts-produce-invalid-ring.md',
                    'tscircuit-issues/092-power-width-review-omits-switching-ripple.md',
                    'tscircuit-issues/093-process-audit-omits-overlapping-legends.md',
                    'tscircuit-issues/094-named-schematic-arrangement-produces-invalid-json.md'])
    selected.add(evidence / 'failed-route10/circuit.json')
    selected.add(evidence / 'failed-route20-visual/process-review.json')
    selected.add(evidence / 'sourcing-check.json')
    if options.include_product_assembly:
        verify_native_product(options)
        mechanical_review = json.loads((ROOT/'product/mechanical-model-review.json').read_text())
        if mechanical_review['source_plans_sha256'] != hashlib.sha256((ROOT/'product/print-plans.json').read_bytes()).hexdigest():
            raise ValueError('Imported mechanical models belong to stale plans')
        for model in mechanical_review['models']:
            if hashlib.sha256((ROOT/model['file']).read_bytes()).hexdigest() != model['sha256']:
                raise ValueError('Imported mechanical model differs from reviewed bytes')
        model_review = json.loads((ROOT/'product/pcb-model-review.json').read_text())
        if model_review['sourceCircuitSha256'] != circuit_sha256 or model_review['emptyModels']:
            raise ValueError('Product PCB models are stale or incomplete')
        for asset in model_review['nativeAssets']:
            model = ROOT/'product/models'/asset['filename']
            if hashlib.sha256(model.read_bytes()).hexdigest() != asset['sha256']:
                raise ValueError('Product model differs from reviewed bytes')
        mesh_review = json.loads((ROOT/'product/print-mesh-review.json').read_text())
        for part in mesh_review['printed_parts']:
            path = ROOT/part['file']
            if not part['watertight'] or not part['winding_consistent'] or hashlib.sha256(path.read_bytes()).hexdigest() != part['sha256']:
                raise ValueError('Product print mesh did not pass readback')
        selected.update(source_closure(ROOT/'enclosure.circuit.tsx'))
        selected.update(source_closure(ROOT/'product.assembly.tsx'))
        selected.update(source_closure(ROOT/'product.exploded.tsx'))
        selected.update(source_closure(ROOT/'product.geometry.tsx'))
        selected.update(source_closure(ROOT/'product/viewer.tsx'))
        selected.add(ROOT/'product/mechanical-model-review.json')
        selected.update(ROOT/'product'/name for name in ['README.md','closed.png','exploded.png','phone-facing.png',
                        'print-requirements.txt','print-plans.json','print-mesh-review.json'])
        selected.update((ROOT/'product').glob('studio-*.png'))
        selected.update((ROOT/'product').glob('studio-*-review.json'))
        selected.update(ROOT/part['file'] for part in mesh_review['printed_parts'])
        selected.update(ROOT/'dist'/name/'circuit.json' for name in ['product.assembly','product.exploded'])
        selected.add(ROOT/'dist/enclosure/circuit.json')
        selected.add(ROOT/'dist/enclosure/3d.png')
        selected.add(ROOT/'cloud/run-heavy.py')
    selected.update(supplemental_files(options, circuit_sha256))
    selected.update(evidence / name for name in ['REPRODUCE-COMMANDS.json',
                    'placement-preservation.json', 'preservation.json', 'visual-review/inspection.json'])
    fabrication = ROOT / 'fabrication' / evidence.name
    selected.update(path for path in fabrication.rglob('*') if path.is_file())
    for path in selected:
        if not path.is_file():
            raise ValueError(f'Required registry input absent: {path.relative_to(ROOT)}')
        if path.stat().st_size > 5_000_000:
            raise ValueError(f'Registry input exceeds bounded file size: {path.relative_to(ROOT)}')
    destination.mkdir(parents=True)
    hashes = {}
    for source in sorted(selected):
        relative = source.relative_to(ROOT)
        target = destination / relative
        target.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(source, target)
        hashes[str(relative)] = hashlib.sha256(target.read_bytes()).hexdigest()
    manifest = {'status': 'R8 side-actuated shutter with standard JST UART; physical tests pending',
                'package': package['name'], 'version': package['version'],
                'source_sha256': hashes, 'total_bytes': sum(path.stat().st_size for path in selected)}
    (destination / 'registry-source-manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
    print(f'{len(hashes)} exact source/build files; {manifest["total_bytes"]} bytes; {destination}')


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--destination', type=Path, required=True)
    parser.add_argument('--evidence', type=Path, required=True)
    parser.add_argument('--supplemental-file', type=Path, action='append', default=[],
                        help='Current review or programmer artifact to publish with the unchanged PCB')
    parser.add_argument("--include-product-assembly", action="store_true")
    args = parser.parse_args()
    stage(args.destination, StageOptions(args.evidence, tuple(args.supplemental_file), args.include_product_assembly))