// containment.el — the Swarm Architecture containment rules, enforced.
//
// "These rules are not conventions. They are enforced by the runtime."
// (Swarm Architecture §3.2). The three rules that make bounded parallelism —
// and therefore location-independent distribution — safe:
//
//   Rule 1: a worker may NOT join another swarm.
//   Rule 2: a worker may NOT initiate a new swarm.
//   Rule 3: a worker may NOT communicate laterally with sibling workers.
//
// Enforcement is by SCOPE TOKEN. When a swarm fans out, the coordinator mints a
// swarm scope token and stamps a distinct worker scope token into each worker's
// task envelope. Any attempt to create or join a swarm checks the caller's
// token: if the caller already holds a WORKER token, the operation is rejected.
// Rule 3 is enforced structurally elsewhere — workers share no mutable state and
// the only channels they hold are the vertical result path — but this module
// provides the explicit lateral-edge check for the execution tree.
//
// A scope token is a JSON object: {"kind":"coordinator|worker","swarm":"<corr>",
// "worker":"<id-or-empty>","depth":"<n>"}.

// ── Token minting ────────────────────────────────────────────────────────────

// CAPABILITIES. A scope token carries a `caps` set — the authority it holds.
// This is an AUTHORITY gate, not a health gate: capability is decided at mint
// time and cannot be acquired at runtime. Engram-WRITE (op_write/op_relate/
// op_supersede -> POST /api/nodes, /api/edges, DELETE) and dharma_emit are
// @manager-ONLY capabilities — exactly the VBD rule that only the orchestrator
// mutates global state. The orchestrator's token carries them; a worker's token
// NEVER does. A worker is therefore STRUCTURALLY UNABLE to mutate global engram
// state, regardless of engram health.
fn cap_orchestrator() -> String { return "engram:read,engram:write,dharma:emit,state:write" }
fn cap_worker() -> String { return "engram:read" }

// containment_coordinator_token — the token the orchestrator (@manager) holds.
// Depth 0. Carries the engram-WRITE + dharma-emit capabilities (@manager-only).
fn containment_coordinator_token(corr_id: String) -> String {
    let kv: [String] = el_list_empty()
    let kv = el_list_append(kv, "kind")
    let kv = el_list_append(kv, "coordinator")
    let kv = el_list_append(kv, "swarm")
    let kv = el_list_append(kv, corr_id)
    let kv = el_list_append(kv, "worker")
    let kv = el_list_append(kv, "")
    let kv = el_list_append(kv, "depth")
    let kv = el_list_append(kv, "0")
    let kv = el_list_append(kv, "caps")
    let kv = el_list_append(kv, cap_orchestrator())
    return json_build_object(kv)
}

// containment_worker_token — the token stamped into a worker's envelope. Depth 1.
// A closed boundary: forbids opening/joining swarms AND carries ONLY the
// engram:READ capability — no engram:write, no dharma:emit. Read-only against the
// full engram; may write only its own local geometry (its returned result).
fn containment_worker_token(corr_id: String, worker_id: String) -> String {
    let kv: [String] = el_list_empty()
    let kv = el_list_append(kv, "kind")
    let kv = el_list_append(kv, "worker")
    let kv = el_list_append(kv, "swarm")
    let kv = el_list_append(kv, corr_id)
    let kv = el_list_append(kv, "worker")
    let kv = el_list_append(kv, worker_id)
    let kv = el_list_append(kv, "depth")
    let kv = el_list_append(kv, "1")
    let kv = el_list_append(kv, "caps")
    let kv = el_list_append(kv, cap_worker())
    return json_build_object(kv)
}

// containment_has_cap — does this token carry capability `cap`?
fn containment_has_cap(token: String, cap: String) -> Bool {
    return str_contains(json_get_string(token, "caps"), cap)
}

// ── Rule checks (return "" on allow, or a rejection reason string) ───────────

// containment_check_open — may the holder of `token` OPEN a new swarm?
// Enforces Rule 2 (a worker may not initiate a new swarm). Only a coordinator
// token, or an absent token (top-level process), may open one.
fn containment_check_open(token: String) -> String {
    if str_eq(token, "") {
        return ""
    }
    let kind: String = json_get_string(token, "kind")
    if str_eq(kind, "worker") {
        return "CONTAINMENT rule 2: a swarm worker may not initiate a new swarm (worker=" + json_get_string(token, "worker") + " swarm=" + json_get_string(token, "swarm") + ")"
    }
    return ""
}

// containment_check_join — may the holder of `token` JOIN swarm `target_corr`?
// Enforces Rule 1 (a worker may not join another swarm). A worker already bound
// to swarm A may not register into swarm B; and a worker may not re-join at all.
fn containment_check_join(token: String, target_corr: String) -> String {
    if str_eq(token, "") {
        return ""
    }
    let kind: String = json_get_string(token, "kind")
    if str_eq(kind, "worker") {
        return "CONTAINMENT rule 1: a swarm worker may not join another swarm (worker=" + json_get_string(token, "worker") + " bound-swarm=" + json_get_string(token, "swarm") + " attempted-swarm=" + target_corr + ")"
    }
    return ""
}

// containment_check_lateral — may `from_token` open a communication edge to a
// sibling worker `to_worker_id`? Enforces Rule 3 (no lateral communication).
// The only permitted edges are vertical: worker->coordinator and
// coordinator->worker. Any worker->worker edge is rejected.
fn containment_check_lateral(from_token: String, to_worker_id: String) -> String {
    let kind: String = json_get_string(from_token, "kind")
    if str_eq(kind, "worker") {
        if str_eq(to_worker_id, "") {
            // empty target = the coordinator (vertical) — allowed
            return ""
        }
        return "CONTAINMENT rule 3: a swarm worker may not communicate laterally with sibling workers (from=" + json_get_string(from_token, "worker") + " to=" + to_worker_id + ")"
    }
    return ""
}

// containment_check_engram_write — RULE 4: only a token carrying the
// engram:write capability (the orchestrator's) may mutate global engram state.
// A worker token (engram:read only) is REJECTED — the authority gate. Reuses the
// exact scope-token mechanism as Rule 2's open-denial. Returns "" on allow, or a
// rejection reason. This is an AUTHORITY gate: it does not consult engram health.
fn containment_check_engram_write(token: String, op: String) -> String {
    if containment_has_cap(token, "engram:write") {
        return ""
    }
    return "CONTAINMENT rule 4: engram-write is @manager-only — a worker is read-only against the engram and may not mutate global state (op=" + op + " kind=" + json_get_string(token, "kind") + " worker=" + json_get_string(token, "worker") + " caps=" + json_get_string(token, "caps") + ")"
}

// containment_check_dharma_emit — the same @manager-only rule for dharma_emit,
// grounding Rule 4 in VBD: global-state mutations (engram-write, dharma-emit) are
// orchestrator-only, checked by the one capability mechanism.
fn containment_check_dharma_emit(token: String) -> String {
    if containment_has_cap(token, "dharma:emit") {
        return ""
    }
    return "CONTAINMENT rule 4: dharma_emit is @manager-only (kind=" + json_get_string(token, "kind") + ")"
}

// ── Enforcement helpers ──────────────────────────────────────────────────────

// containment_allows_open — Bool convenience over containment_check_open.
fn containment_allows_open(token: String) -> Bool {
    return str_eq(containment_check_open(token), "")
}

// containment_is_worker — is this a worker-scoped (closed-boundary) token?
fn containment_is_worker(token: String) -> Bool {
    return str_eq(json_get_string(token, "kind"), "worker")
}

// containment_guard_open — assert a swarm may be opened under this token.
// Returns "" if allowed, or records a CONTAINMENT violation to the work-tracking
// journal and returns the reason. Callers must abort on a non-empty return.
fn containment_guard_open(token: String, corr_id: String) -> String {
    let reason: String = containment_check_open(token)
    if str_eq(reason, "") {
        return ""
    }
    let p: String = json_set_str("{}", "reason", reason)
    worktrack_append("containment.violation", corr_id, "open", p)
    return reason
}

// containment_guard_engram_write — assert a token may mutate global engram state
// (Rule 4). Returns "" if allowed; otherwise journals a containment.violation and
// returns the reason. The write path MUST abort on a non-empty return.
fn containment_guard_engram_write(token: String, corr_id: String, op: String) -> String {
    let reason: String = containment_check_engram_write(token, op)
    if str_eq(reason, "") {
        return ""
    }
    let p0: String = json_set_str("{}", "reason", reason)
    let p1: String = json_set_str(p0, "op", op)
    worktrack_append("containment.violation", corr_id, "engram-write", p1)
    return reason
}
