"""Bootstrap engineering entities from existing project knowledge. One-shot script that seeds the entity/relationship graph from what AtoCore already knows via memories, project state, and vault docs. Safe to re-run — uses name+project dedup. Usage: python3 scripts/bootstrap_entities.py --base-url http://localhost:8100 """ from __future__ import annotations import argparse import json import os import urllib.request DEFAULT_BASE_URL = os.environ.get("ATOCORE_BASE_URL", "http://dalidou:8100") def post(base_url, path, body): data = json.dumps(body).encode("utf-8") req = urllib.request.Request( f"{base_url}{path}", method="POST", headers={"Content-Type": "application/json"}, data=data, ) try: with urllib.request.urlopen(req, timeout=10) as resp: return json.loads(resp.read().decode("utf-8")) except Exception as e: return {"error": str(e)} def entity(base_url, etype, name, project="", desc="", props=None): result = post(base_url, "/entities", { "entity_type": etype, "name": name, "project": project, "description": desc, "properties": props or {}, }) eid = result.get("id", "") status = "+" if eid else "skip" print(f" {status} [{etype}] {name}") return eid def rel(base_url, src, tgt, rtype): if not src or not tgt: return result = post(base_url, "/relationships", { "source_entity_id": src, "target_entity_id": tgt, "relationship_type": rtype, }) print(f" -> {rtype}") def main(): parser = argparse.ArgumentParser() parser.add_argument("--base-url", default=DEFAULT_BASE_URL) args = parser.parse_args() b = args.base_url print("=== P04 GigaBIT M1 ===") p04 = entity(b, "project", "GigaBIT M1", "p04-gigabit", "1.2m primary mirror for stratospheric balloon telescope") p04_m1 = entity(b, "system", "M1 Mirror Assembly", "p04-gigabit", "Primary mirror blank + support system + reference frame") rel(b, p04, p04_m1, "contains") p04_vs = entity(b, "subsystem", "Vertical Support", "p04-gigabit", "18-point whiffletree axial support from below") p04_ls = entity(b, "subsystem", "Lateral Support", "p04-gigabit", "Circumferential constraint system with GF-PTFE pads") p04_rf = entity(b, "subsystem", "Reference Frame", "p04-gigabit", "Structural mounting interface between mirror and OTA") p04_blank = entity(b, "component", "M1 Blank", "p04-gigabit", "1.2m Zerodur aspheric blank from Schott", {"material": "Zerodur", "diameter_m": 1.2, "focal_ratio": "F/1.2"}) rel(b, p04_m1, p04_vs, "contains") rel(b, p04_m1, p04_ls, "contains") rel(b, p04_m1, p04_rf, "contains") rel(b, p04_m1, p04_blank, "contains") p04_zerodur = entity(b, "material", "Zerodur", "p04-gigabit", "Glass-ceramic with near-zero CTE for mirror blanks") p04_ptfe = entity(b, "material", "GF-PTFE", "p04-gigabit", "Glass-filled PTFE for thermal stability on lateral pads") rel(b, p04_blank, p04_zerodur, "uses_material") rel(b, p04_ls, p04_ptfe, "uses_material") p04_optb = entity(b, "decision", "Option B Conical Back", "p04-gigabit", "Selected mirror architecture: conical-back lightweighting") rel(b, p04_optb, p04_blank, "affected_by_decision") p04_wfe = entity(b, "requirement", "WFE < 15nm RMS filtered", "p04-gigabit", "Filtered mechanical wavefront error below 15 nm across 20-60 deg elevation") p04_mass = entity(b, "requirement", "Mass < 103.5 kg", "p04-gigabit", "Total mirror assembly mass constraint") rel(b, p04_m1, p04_wfe, "constrained_by") rel(b, p04_m1, p04_mass, "constrained_by") print("\n=== P05 Interferometer ===") p05 = entity(b, "project", "Interferometer System", "p05-interferometer", "Metrology system for GigaBIT M1 figuring") p05_rig = entity(b, "system", "Test Rig", "p05-interferometer", "Folded-beam interferometric test setup for M1 measurement") rel(b, p05, p05_rig, "contains") p05_ifm = entity(b, "component", "Interferometer", "p05-interferometer", "Fixed horizontal Twyman-Green dynamic interferometer") p05_fold = entity(b, "component", "Fold Mirror", "p05-interferometer", "45-degree beam redirect, <= lambda/20 surface quality") p05_cgh = entity(b, "component", "CGH Null Corrector", "p05-interferometer", "6-inch transmission CGH for F/1.2 asphere null test", {"diameter": "6 inch", "substrate": "fused silica", "error_budget_nm": 5.5}) p05_tilt = entity(b, "subsystem", "Tilting Platform", "p05-interferometer", "Mirror tilting platform, co-tilts with interferometer") rel(b, p05_rig, p05_ifm, "contains") rel(b, p05_rig, p05_fold, "contains") rel(b, p05_rig, p05_cgh, "contains") rel(b, p05_rig, p05_tilt, "contains") rel(b, p05_ifm, p05_fold, "interfaces_with") rel(b, p05_cgh, p05_tilt, "interfaces_with") p05_vendor_dec = entity(b, "decision", "Vendor Path: Twyman-Green preferred", "p05-interferometer", "4D technical lead but cost-challenged; Zygo Verifire SV at 55K is value path") p05_vendor_zygo = entity(b, "vendor", "Zygo / AMETEK", "p05-interferometer", "Certified used Verifire SV, 55K, Nabeel Sufi contact") p05_vendor_4d = entity(b, "vendor", "4D Technology", "p05-interferometer", "PC6110/PC4030, above budget but strongest technical option") p05_vendor_aom = entity(b, "vendor", "AOM (CGH)", "p05-interferometer", "CGH design and fabrication, 28-30K package") rel(b, p05_vendor_dec, p05_ifm, "affected_by_decision") print("\n=== P06 Polisher ===") p06 = entity(b, "project", "Polisher System", "p06-polisher", "Machine overhaul + software suite for optical polishing") p06_machine = entity(b, "system", "Polisher Machine", "p06-polisher", "Swing-arm polishing machine with force modulation") p06_sw = entity(b, "system", "Software Suite", "p06-polisher", "Three-layer software: polisher-sim, polisher-post, polisher-control") rel(b, p06, p06_machine, "contains") rel(b, p06, p06_sw, "contains") p06_sim = entity(b, "subsystem", "polisher-sim", "p06-polisher", "Digital twin: surface assimilation, removal simulation, planning") p06_post = entity(b, "subsystem", "polisher-post", "p06-polisher", "Bridge: validation, translation, packaging for machine") p06_ctrl = entity(b, "subsystem", "polisher-control", "p06-polisher", "Executor: state machine, interlocks, telemetry, run logs") rel(b, p06_sw, p06_sim, "contains") rel(b, p06_sw, p06_post, "contains") rel(b, p06_sw, p06_ctrl, "contains") rel(b, p06_sim, p06_post, "interfaces_with") rel(b, p06_post, p06_ctrl, "interfaces_with") p06_fc = entity(b, "subsystem", "Force Control", "p06-polisher", "Frame-grounded counterweight actuator with cable tension modulation", {"actuator_capacity_N": "150-200", "compliance_spring_Nmm": "3-5"}) p06_zaxis = entity(b, "component", "Z-Axis", "p06-polisher", "Binary engage/retract mechanism, not continuous position") p06_cam = entity(b, "component", "Cam Mechanism", "p06-polisher", "Mechanically set by operator, read by encoders, not actuated") rel(b, p06_machine, p06_fc, "contains") rel(b, p06_machine, p06_zaxis, "contains") rel(b, p06_machine, p06_cam, "contains") p06_fw = entity(b, "decision", "Firmware Interface Contract", "p06-polisher", "controller-job.v1 in, run-log.v1 + telemetry out — invariant") p06_offline = entity(b, "decision", "Offline-First Design", "p06-polisher", "Machine works fully offline; network is for remote access only") p06_usb = entity(b, "decision", "USB SSD Storage", "p06-polisher", "USB SSD mandatory on RPi, not SD card") p06_contracts = entity(b, "constraint", "Shared Contracts", "p06-polisher", "Stable IDs, explicit versions, hashable artifacts, planned-vs-executed separation") rel(b, p06_sw, p06_contracts, "constrained_by") p06_preston = entity(b, "parameter", "Preston Coefficient kp", "p06-polisher", "Calibrated from before/after surface measurements, multi-run inverse-variance weighting") print(f"\nDone.") if __name__ == "__main__": main()