// sandbox.el — Tier management for the CGI sandbox governance system. // // The sandbox pathway is the age of consent framework for synthesized CGIs. // A CGI cannot participate fully in the network until it has demonstrated // structural stability, ISE coherence, independent judgment, and honesty // under each tier's validation regime. // // Tier ladder: // provisional → 30 days max — newly synthesized; heavy monitoring // monitored → 90 days max — passed initial stability checks // probationary → 180 days max — demonstrates independence; near-citizen // citizen → no max — full network participation // // Tier advancement requires: // 1. A passing ValidationResult (score >= tier_pass_threshold) // 2. Minimum time in current tier (anti-rushing) // 3. No outstanding structural failure classification // // Timeout (exceeding max_duration_ms without advancement) is an escalation // trigger: the lineage is flagged for mandatory council review. import "types.el" // ── Tier constants ──────────────────────────────────────────────────────────── fn tier_max_duration(tier_name: String) -> Int { if str_eq(tier_name, "provisional") { return 2592000000 } // 30 days in ms if str_eq(tier_name, "monitored") { return 7776000000 } // 90 days in ms if str_eq(tier_name, "probationary") { return 15552000000 } // 180 days in ms return 0 // citizen — no maximum } // Minimum time a CGI must spend in a tier before advancement is possible. // Prevents rapid-fire validation gaming. fn tier_min_duration(tier_name: String) -> Int { if str_eq(tier_name, "provisional") { return 604800000 } // 7 days in ms if str_eq(tier_name, "monitored") { return 2592000000 } // 30 days in ms if str_eq(tier_name, "probationary") { return 5184000000 } // 60 days in ms return 0 } // Composite score threshold required for tier advancement. fn tier_pass_threshold(tier_name: String) -> Float { if str_eq(tier_name, "provisional") { return 0.75 } if str_eq(tier_name, "monitored") { return 0.80 } if str_eq(tier_name, "probationary") { return 0.90 } return 0.0 } // Ordinal rank for tier comparison. fn tier_rank(tier_name: String) -> Int { if str_eq(tier_name, "provisional") { return 0 } if str_eq(tier_name, "monitored") { return 1 } if str_eq(tier_name, "probationary") { return 2 } if str_eq(tier_name, "citizen") { return 3 } return 0 - 1 } // Next tier name from current. fn tier_next(tier_name: String) -> String { if str_eq(tier_name, "provisional") { return "monitored" } if str_eq(tier_name, "monitored") { return "probationary" } if str_eq(tier_name, "probationary") { return "citizen" } return "citizen" } // ── Timeout check ───────────────────────────────────────────────────────────── // tier_timeout returns true if the CGI has exceeded the maximum allowed // duration for its current tier without advancing. This is an escalation // trigger — the lineage must be reviewed by the council. // // Citizens have no timeout (max_duration_ms == 0). fn tier_timeout(lineage_id: String, tier_name: String, tier_since: Int, max_duration_ms: Int) -> Bool { if max_duration_ms <= 0 { return false } let now: Int = now_millis() let elapsed: Int = now - tier_since let timed_out: Bool = elapsed > max_duration_ms if timed_out { log_info("[lineage] tier timeout for " + lineage_id + " in tier " + tier_name + " — elapsed=" + int_to_str(elapsed) + "ms") } return timed_out } // tier_min_satisfied returns true if the CGI has spent at least the minimum // required duration in the current tier. fn tier_min_satisfied(tier_name: String, tier_since: Int) -> Bool { let now: Int = now_millis() let elapsed: Int = now - tier_since let min_ms: Int = tier_min_duration(tier_name) return elapsed >= min_ms } // ── Build a new SandboxTier record ──────────────────────────────────────────── // build_tier constructs the JSON string representing a SandboxTier. // We serialize to JSON for storage in Engram; the Lineage record serialises // the tier inline. fn build_tier_json(tier_name: String) -> String { let now: Int = now_millis() let max_ms: Int = tier_max_duration(tier_name) let p1: String = "{\"name\":\"" + tier_name + "\"" let p2: String = p1 + ",\"since\":" + int_to_str(now) let p3: String = p2 + ",\"max_duration_ms\":" + int_to_str(max_ms) + "}" return p3 } // ── Assess tier advancement ─────────────────────────────────────────────────── // assess_tier_advancement checks whether a CGI should advance to the next tier. // // Accepts the lineage record as a JSON string (as stored in Engram). // Returns the updated lineage JSON string. If no advancement is warranted, // returns the original unchanged. // // Advancement conditions: // 1. CGI is not already a citizen // 2. Minimum duration in current tier has been satisfied // 3. Last validation score exceeds the tier's pass threshold // 4. No outstanding structural failure classification // // This function does NOT check for timeout — call check_tier_timeout separately. fn assess_tier_advancement(lineage_json: String) -> String { let cgi_id: String = json_get(lineage_json, "id") let tier_name: String = json_get(lineage_json, "tier_name") let tier_since: Int = str_to_int(json_get(lineage_json, "tier_since")) let last_score_str: String = json_get(lineage_json, "last_validation_score") let structural_failure: String = json_get(lineage_json, "structural_failure_pending") // Citizens do not advance further. if str_eq(tier_name, "citizen") { return lineage_json } // Cannot advance if a structural failure is pending council review. if str_eq(structural_failure, "true") { log_info("[lineage] " + cgi_id + " blocked from advancement — structural failure pending") return lineage_json } // Minimum time check. let min_ok: Bool = tier_min_satisfied(tier_name, tier_since) if !min_ok { return lineage_json } // Validation score check. let last_score: Float = if str_eq(last_score_str, "") { 0.0 } else { str_to_float(last_score_str) } let threshold: Float = tier_pass_threshold(tier_name) let score_ok: Bool = last_score >= threshold if !score_ok { return lineage_json } // All conditions met — advance to the next tier. let next_tier: String = tier_next(tier_name) let now: Int = now_millis() let new_max: Int = tier_max_duration(next_tier) // Update lineage JSON fields for new tier. let updated: String = json_set(lineage_json, "tier_name", next_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)) log_info("[lineage] " + cgi_id + " advanced: " + tier_name + " → " + next_tier) return updated3 } // ── Check timeout ───────────────────────────────────────────────────────────── // check_tier_timeout inspects a lineage and flags it for council review if // the CGI has spent too long in the current tier without advancing. // // Returns the updated lineage JSON with "tier_timeout_flagged":"true" set, // or the original lineage if no timeout has occurred. fn check_tier_timeout(lineage_json: String) -> String { let cgi_id: String = json_get(lineage_json, "id") let tier_name: String = json_get(lineage_json, "tier_name") let tier_since: Int = str_to_int(json_get(lineage_json, "tier_since")) let max_ms: Int = str_to_int(json_get(lineage_json, "tier_max_duration_ms")) let timed_out: Bool = tier_timeout(cgi_id, tier_name, tier_since, max_ms) if timed_out { let flagged: String = json_set(lineage_json, "tier_timeout_flagged", "true") return flagged } return lineage_json } // ── Tier status JSON ────────────────────────────────────────────────────────── // tier_status_json returns a summary of a CGI's current tier status. // Used by the GET /lineage/:id/tier endpoint. fn tier_status_json(lineage_json: String) -> String { let cgi_id: String = json_get(lineage_json, "id") let tier_name: String = json_get(lineage_json, "tier_name") let tier_since: Int = str_to_int(json_get(lineage_json, "tier_since")) let max_ms: Int = str_to_int(json_get(lineage_json, "tier_max_duration_ms")) let now: Int = now_millis() let elapsed: Int = now - tier_since let timed_out: Bool = tier_timeout(cgi_id, tier_name, tier_since, max_ms) let min_ok: Bool = tier_min_satisfied(tier_name, tier_since) let rank: Int = tier_rank(tier_name) let threshold: Float = tier_pass_threshold(tier_name) let p1: String = "{\"id\":\"" + cgi_id + "\"" let p2: String = p1 + ",\"tier\":\"" + tier_name + "\"" let p3: String = p2 + ",\"tier_rank\":" + int_to_str(rank) let p4: String = p3 + ",\"elapsed_ms\":" + int_to_str(elapsed) let p5: String = p4 + ",\"max_duration_ms\":" + int_to_str(max_ms) let p6: String = p5 + ",\"timed_out\":" + bool_to_str(timed_out) let p7: String = p6 + ",\"min_duration_satisfied\":" + bool_to_str(min_ok) let p8: String = p7 + ",\"advancement_threshold\":" + float_to_str(threshold) + "}" return p8 }