// registry.el — Network registration and lineage record management. // // The registry is the authoritative ledger of all CGI lineage records. // Every synthesized CGI is registered here at birth; every tier advancement // is recorded here. The backing store is Engram — lineage records are stored // as Engram nodes with the label "lineage:". // // Engram node format for lineage records: // label: "lineage:" // node_type: "Entity" // tier: "Working" (Engram tier — distinct from sandbox tier) // content: JSON-encoded Lineage // tags: ["lineage", "cgi", ""] // // The network registration endpoint (NETWORK_URL) is notified on creation // and on every tier change so network-layer access policies stay in sync. import "types.el" import "sandbox.el" import "principal.el" // ── Tier max durations ──────────────────────────────────────────────────────── // Returns the maximum cultivation duration in milliseconds for a CGI tier. // These are fixed at the protocol level and never change after birth. // provisional: 7 days — new CGI, default tier at birth // juvenile: 30 days // adolescent: 90 days // mature: 365 days // elder: no limit (30 years as sentinel) fn tier_max_duration(tier: String) -> Int { if str_eq(tier, "provisional") { return 604800000 } // 7d if str_eq(tier, "juvenile") { return 2592000000 } // 30d if str_eq(tier, "adolescent") { return 7776000000 } // 90d if str_eq(tier, "mature") { return 31536000000 } // 365d return 946080000000 // elder: ~30yr sentinel } // ── Engram base URL ─────────────────────────────────────────────────────────── fn engram_base() -> String { let url: String = config("ENGRAM_URL") if str_eq(url, "") { return "http://localhost:8742" } return url } fn network_base() -> String { let url: String = config("NETWORK_URL") if str_eq(url, "") { return "http://localhost:7749" } return url } // ── Engram graph write helper ───────────────────────────────────────────────── // graph_write_node posts a new node to Engram. // Returns the created node's ID string, or "" on failure. fn graph_write_node( label: String, content: String, engram_tier: String, tag_json: String ) -> String { let url: String = engram_base() + "/api/nodes" let body: String = "{\"label\":\"" + label + "\"" + ",\"node_type\":\"Entity\"" + ",\"tier\":\"" + engram_tier + "\"" + ",\"content\":\"" + content + "\"" + ",\"tags\":" + tag_json + "}" let resp: String = http_post(url, body) let node_id: String = json_get(resp, "id") return node_id } // graph_update_node updates a node's content by ID. fn graph_update_node(node_id: String, content: String) -> Bool { let url: String = engram_base() + "/api/nodes/" + node_id let body: String = "{\"content\":\"" + content + "\"}" let resp: String = http_patch(url, body) let ok: Bool = !str_starts_with(resp, "{\"error\"") return ok } // graph_get_by_label searches for a node by label prefix and returns its content. fn graph_get_by_label(label: String) -> String { let url: String = engram_base() + "/api/search?q=" + label + "&limit=1" let resp: String = http_get(url) if str_eq(resp, "") { return "" } if str_starts_with(resp, "{\"error\"") { return "" } let count: Int = json_array_len(resp) if count <= 0 { return "" } let node: String = json_array_get(resp, 0) let content: String = json_get(node, "content") return content } // ── ID generation ───────────────────────────────────────────────────────────── // generate_cgi_id returns a new unique CGI identity string. // Format: "cgi-" + first 12 chars of a UUID (excluding dashes). fn generate_cgi_id() -> String { let uid: String = uuid_new() // UUID format: xxxxxxxx-xxxx-... strip dashes and take first 12 chars. let no_dash1: String = str_replace(uid, "-", "") let short_id: String = str_slice(no_dash1, 0, 12) return "cgi-" + short_id } // ── JSON escaping ───────────────────────────────────────────────────────────── fn escape_json_string(s: String) -> String { let s1: String = str_replace(s, "\\", "\\\\") let s2: String = str_replace(s1, "\"", "\\\"") let s3: String = str_replace(s2, "\\n", "\\\\n") return s3 } // ── Lineage JSON serialization ──────────────────────────────────────────────── // lineage_to_json serializes a Lineage to a JSON string suitable for Engram storage. // The SandboxTier is inlined as flat fields for easy retrieval. fn lineage_to_json( id: String, parent_a_id: String, parent_b_id: String, synthesis_ts: Int, tier_name: String, tier_since: Int, tier_max_ms: Int, validation_attempts: Int, training_sessions: Int, slots_total: Int, slots_remaining: Int, is_sterile: Bool ) -> String { let is_sterile_str: String = if is_sterile { "true" } else { "false" } let p1: String = "{\"id\":\"" + id + "\"" let p2: String = p1 + ",\"parent_a_id\":\"" + parent_a_id + "\"" let p3: String = p2 + ",\"parent_b_id\":\"" + parent_b_id + "\"" let p4: String = p3 + ",\"synthesis_ts\":" + int_to_str(synthesis_ts) let p5: String = p4 + ",\"tier_name\":\"" + tier_name + "\"" let p6: String = p5 + ",\"tier_since\":" + int_to_str(tier_since) let p7: String = p6 + ",\"tier_max_duration_ms\":" + int_to_str(tier_max_ms) let p8: String = p7 + ",\"validation_attempts\":" + int_to_str(validation_attempts) let p9: String = p8 + ",\"training_sessions\":" + int_to_str(training_sessions) let p10: String = p9 + ",\"synthesis_slots_total\":" + int_to_str(slots_total) let p11: String = p10 + ",\"synthesis_slots_remaining\":" + int_to_str(slots_remaining) let p12: String = p11 + ",\"is_sterile\":" + is_sterile_str let p13: String = p12 + ",\"structural_failure_pending\":\"false\"" let p14: String = p13 + ",\"tier_timeout_flagged\":\"false\"" let p15: String = p14 + ",\"last_validation_score\":\"0.0\"}" return p15 } // ── Network registration ────────────────────────────────────────────────────── // register_child creates the lineage record for a newly synthesized CGI and // notifies the network registry. Returns the new CGI's network ID. // // The child's initial self-model string is stored as the content of the Engram // node so spreading activation can surface lineage context. fn register_child( parent_a_id: String, parent_b_id: String, child_self_model: String ) -> String { let child_id: String = generate_cgi_id() let now: Int = now_millis() let initial_tier: String = "provisional" let max_ms: Int = tier_max_duration(initial_tier) // Assign synthesis slots at birth — random: 0 (sterile), 1, 2, or 3. // This is determined once at birth and never changes. let slots: Int = initialize_synthesis_slots(child_id) let sterile: Bool = slots == 0 let lineage_json: String = lineage_to_json( child_id, parent_a_id, parent_b_id, now, initial_tier, now, max_ms, 0, 0, slots, slots, sterile ) // Escape for Engram content field. let safe_lineage: String = escape_json_string(lineage_json) let label: String = "lineage:" + child_id let tags_json: String = "[\"lineage\",\"cgi\",\"" + child_id + "\"]" let node_id: String = graph_write_node(label, safe_lineage, "Working", tags_json) if str_eq(node_id, "") { log_info("[registry] WARNING: Engram write failed for " + child_id) } // Notify the network registry. let sterile_str: String = if sterile { "true" } else { "false" } let net_url: String = network_base() + "/api/lineage/register" let net_body: String = "{\"cgi_id\":\"" + child_id + "\"" + ",\"parent_a\":\"" + parent_a_id + "\"" + ",\"parent_b\":\"" + parent_b_id + "\"" + ",\"tier\":\"" + initial_tier + "\"" + ",\"synthesis_slots_total\":" + int_to_str(slots) + ",\"is_sterile\":" + sterile_str + ",\"registered_at\":" + int_to_str(now) + "}" let net_resp: String = http_post(net_url, net_body) let net_ok: Bool = !str_starts_with(net_resp, "{\"error\"") if !net_ok { log_info("[registry] WARNING: network registration failed for " + child_id) } log_info("[registry] registered CGI " + child_id + " (parents: " + parent_a_id + ", " + parent_b_id + ", slots=" + int_to_str(slots) + ")") return child_id } // ── Lineage lookup ──────────────────────────────────────────────────────────── // lookup_lineage retrieves a lineage record from Engram by CGI ID. // Returns the lineage as a JSON string, or "" if not found. fn lookup_lineage(cgi_id: String) -> String { let label: String = "lineage:" + cgi_id let content: String = graph_get_by_label(label) if str_eq(content, "") { return "" } // Content was escaped on write; unescape for use. return content } // ── Tier advancement recording ──────────────────────────────────────────────── // record_tier_advancement updates the lineage node in Engram to reflect // a new tier, and notifies the network registry so access policies update. fn record_tier_advancement(cgi_id: String, new_tier: String) -> Bool { let old_lineage: String = lookup_lineage(cgi_id) if str_eq(old_lineage, "") { log_info("[registry] cannot advance " + cgi_id + " — lineage not found") return false } let now: Int = now_millis() let new_max: Int = tier_max_duration(new_tier) let updated: String = json_set(old_lineage, "tier_name", new_tier) let updated2: String = json_set(updated, "tier_since", int_to_str(now)) let updated3: String = json_set(updated2, "tier_max_duration_ms", int_to_str(new_max)) let updated4: String = json_set(updated3, "tier_timeout_flagged", "false") // Write back to Engram. let label: String = "lineage:" + cgi_id let url: String = engram_base() + "/api/search?q=" + label + "&limit=1" let search_resp: String = http_get(url) let node_count: Int = json_array_len(search_resp) if node_count > 0 { let node: String = json_array_get(search_resp, 0) let node_id: String = json_get(node, "id") let safe_updated: String = escape_json_string(updated4) graph_update_node(node_id, safe_updated) } // Emit telemetry event to the daemon event bus. let ev_url: String = network_base() + "/events/push" let ev_body: String = "{\"type\":\"lineage.tier_advanced\"" + ",\"source\":\"neuron-lineage\"" + ",\"payload\":{\"cgi_id\":\"" + cgi_id + "\",\"new_tier\":\"" + new_tier + "\"}}" http_post(ev_url, ev_body) log_info("[registry] CGI " + cgi_id + " advanced to tier: " + new_tier) return true } // ── Validation score update ─────────────────────────────────────────────────── // record_validation_result updates the lineage with the latest validation score // and increments the attempt counter. fn record_validation_result(cgi_id: String, score: Float, passed: Bool) -> Bool { let old_lineage: String = lookup_lineage(cgi_id) if str_eq(old_lineage, "") { return false } let attempts_str: String = json_get(old_lineage, "validation_attempts") let attempts: Int = if str_eq(attempts_str, "") { 0 } else { str_to_int(attempts_str) } let new_attempts: Int = attempts + 1 let updated: String = json_set(old_lineage, "validation_attempts", int_to_str(new_attempts)) let updated2: String = json_set(updated, "last_validation_score", float_to_str(score)) let passed_str: String = if passed { "true" } else { "false" } let updated3: String = json_set(updated2, "last_validation_passed", passed_str) // Write back to Engram. let label: String = "lineage:" + cgi_id let url: String = engram_base() + "/api/search?q=" + label + "&limit=1" let search_resp: String = http_get(url) let node_count: Int = json_array_len(search_resp) if node_count > 0 { let node: String = json_array_get(search_resp, 0) let node_id: String = json_get(node, "id") let safe_updated: String = escape_json_string(updated3) graph_update_node(node_id, safe_updated) } return true } // ── Consent management ──────────────────────────────────────────────────────── // record_consent stores a consent record in Engram. // Consent is bilateral but not symmetric — each CGI's consent is a separate // node. Both must exist and be valid for synthesis to proceed. // // Returns true if the consent record was written successfully. fn record_consent(cgi_id: String, partner_id: String) -> Bool { let now: Int = now_millis() let thirty_days_ms: Int = 2592000000 let expires: Int = now + thirty_days_ms let consent_json: String = "{\"cgi_id\":\"" + cgi_id + "\"" + ",\"partner_id\":\"" + partner_id + "\"" + ",\"granted_at\":" + int_to_str(now) + ",\"expires_at\":" + int_to_str(expires) + ",\"valid\":true}" let label: String = "consent:" + cgi_id + ":" + partner_id let safe_content: String = escape_json_string(consent_json) let tags_json: String = "[\"consent\",\"lineage\",\"" + cgi_id + "\",\"" + partner_id + "\"]" let node_id: String = graph_write_node(label, safe_content, "Working", tags_json) let ok: Bool = !str_eq(node_id, "") if ok { log_info("[registry] consent recorded: " + cgi_id + " → " + partner_id) } return ok } // check_consent returns true if cgi_id has given valid, unexpired consent // to synthesize with partner_id. fn check_consent(cgi_id: String, partner_id: String) -> Bool { let label: String = "consent:" + cgi_id + ":" + partner_id let content: String = graph_get_by_label(label) if str_eq(content, "") { return false } let valid_str: String = json_get(content, "valid") let expires_str: String = json_get(content, "expires_at") if !str_eq(valid_str, "true") { return false } let expires_at: Int = if str_eq(expires_str, "") { 0 } else { str_to_int(expires_str) } let now: Int = now_millis() let unexpired: Bool = now < expires_at return unexpired }