// swarm.el — Native interruptibility for Neuron-dispatched agents.
//
// CANON: Neuron memory 1ca4d3e9 (native-interruptibility). A dispatched worker
// must be re-steerable OR killable the INSTANT a correction / new signal arrives,
// MID-TASK — like a human who stops the moment they're told "that's wrong", not
// one who finishes the wrong workflow first. Claude's sub-agents cannot do this:
// reset off a wrong path, they finish the current workflow before absorbing the
// correction, wasting work on a KNOWN-WRONG thing. Neuron's must never.
//
// MECHANISM (Will's steer, 2026-08-15 — "not a hard problem, don't over-engineer"):
//   CancellationToken + always-on input.
//   (1) INPUT ALWAYS ON — every worker holds a CONTROL CHANNEL (the token). The
//       coordinator can push a signal at ANY time; the channel is NEVER gated by
//       whether the worker is busy. Like a person who keeps hearing while talking.
//   (2) COOPERATIVE CANCELLATION — the worker CHECKS the token at every step
//       boundary (a non-blocking poll, __channel_try_recv). On a signal it STOPS,
//       ABSORBS the correction (re-plans) or TERMINATES cleanly — with ZERO wasted
//       continuation of known-wrong work.
//
// The SAME mechanism serves the conversational speech loop: a mic barge-in simply
// invokes the token on the running render (see peripheral loop 66155ba9).
//
// SAFETY (Rule-4 single-writer / bounded purview): a worker holds its task as a
// PURVIEW (a meaning-plan). It writes ONLY to its own out-channel and its own
// local purview — it holds NO global write-lock. So it can be interrupted or
// killed mid-step with NO half-committed global state; the coordinator just
// signals stop. Bounded purviews are what make live interruption clean.
//
// Built on the runtime concurrency primitives:
//   __channel_new / __channel_send / __channel_recv / __channel_try_recv  (channels)
//   __thread_create / __thread_join                                       (workers)
//
// Contrast: the pre-existing channel.el `_channel_worker` loop spawns+joins the
// WHOLE task with no signal check — the exact broken pattern. This module checks
// the token BETWEEN every bounded step, so the interrupt latency is one step, not
// one whole workflow.

// ── low-level worker/thread wrappers (call seed prims directly; no collision) ──

fn _swarm_spawn(fn_name: String, arg: String) -> Int {
    return __thread_create(fn_name, arg)
}

fn _swarm_join(tid: Int) -> String {
    return __thread_join(tid)
}

// ── Cancellation token — the always-on control channel ─────────────────────────

// token_new — create a cancellation/pause/redirect token for one worker.
// Unbounded so the coordinator NEVER blocks when it signals (always-on input).
fn token_new() -> Int {
    return __channel_new(0)
}

// ── Coordinator ops — INVOKE the token (interrupt / re-steer / kill) ───────────

// token_signal — send a raw signal JSON. Low-level; prefer the named helpers.
fn token_signal(tok: Int, sig_json: String) {
    __channel_send(tok, sig_json)
}

// token_interrupt — ask the worker to stop at the next step boundary and hold.
fn token_interrupt(tok: Int) {
    __channel_send(tok, "{\"sig\":\"PAUSE\"}")
}

// token_pause — alias for interrupt: stop stepping, hold state, wait for RESUME.
fn token_pause(tok: Int) {
    __channel_send(tok, "{\"sig\":\"PAUSE\"}")
}

// token_resume — resume a paused worker on its held plan.
fn token_resume(tok: Int) {
    __channel_send(tok, "{\"sig\":\"RESUME\"}")
}

// token_kill — terminate the worker cleanly at the next step boundary.
fn token_kill(tok: Int) {
    __channel_send(tok, "{\"sig\":\"KILL\"}")
}

// token_redirect — re-steer the worker onto a NEW plan MID-TASK. Progress already
// made (completed steps, emitted outputs) is PRESERVED; only the remaining plan is
// replaced. new_steps is a JSON array string, e.g. "[\"step-a\",\"step-b\"]".
fn token_redirect(tok: Int, new_goal: String, new_steps: String) {
    let msg: String = "{\"sig\":\"REDIRECT\",\"goal\":\"" +
                      json_escape_string(new_goal) + "\",\"steps\":" + new_steps + "}"
    __channel_send(tok, msg)
}

// ── Worker ops — CHECK the token (cooperative cancellation point) ──────────────

// token_check — non-blocking poll of the token. Returns the pending signal JSON,
// or "" when there is no signal. Called at every step boundary. This is the
// always-on check: it never blocks the worker, so work proceeds at full speed
// until — and only until — a signal actually arrives.
fn token_check(tok: Int) -> String {
    return __channel_try_recv(tok)
}

// token_wait — BLOCK until the next signal. Used only while PAUSED (the worker is
// idle, so blocking is correct — it is not spinning).
fn token_wait(tok: Int) -> String {
    return __channel_recv(tok)
}

// ── Purview — the worker's meaning-plan (held task, resumable) ─────────────────

// purview_new — build a bounded meaning-plan the worker carries.
//   id    — purview id
//   goal  — what the worker is trying to achieve (steerable)
//   steps — JSON array string of step descriptors (the remaining plan)
// A redirect UPDATES this plan; it is not lost.
fn purview_new(id: String, goal: String, steps: String) -> String {
    return "{\"id\":\"" + json_escape_string(id) +
           "\",\"goal\":\"" + json_escape_string(goal) +
           "\",\"steps\":" + steps +
           ",\"idx\":0,\"completed\":0,\"status\":\"ready\"}"
}

// ── The interruptible worker loop ──────────────────────────────────────────────

// swarm_worker_run — the generic natively-interruptible worker.
//
// arg is a JSON object carrying:
//   "tok"     — the cancellation token (control channel handle)
//   "out"     — the worker's own output channel handle (its ONLY write surface)
//   "step_fn" — name of an El fn (String)->String that performs ONE bounded step
//   "purview" — the meaning-plan (from purview_new)
//
// The loop: at EVERY iteration it first polls the token (always-on check). Only
// then does it execute ONE bounded step. So a KILL/REDIRECT/PAUSE is absorbed
// within a single step — never after finishing the whole (possibly wrong) plan.
//
// step_fn is invoked as a child thread joined immediately: exactly ONE step of
// work is in flight, so the interrupt latency is bounded by a single step.
//
// Returns the final purview JSON (status: complete | killed | redirected).
fn swarm_worker_run(arg: String) -> String {
    let tok: Int = str_to_int(json_get(arg, "tok"))
    let out_ch: Int = str_to_int(json_get(arg, "out"))
    let step_fn: String = json_get(arg, "step_fn")
    let purview: String = json_get_raw(arg, "purview")

    let pid: String = json_get(purview, "id")
    let goal: String = json_get(purview, "goal")
    let steps: String = json_get_raw(purview, "steps")
    let n: Int = json_array_len(steps)
    let idx: Int = 0
    let completed: Int = 0
    let status: String = "running"
    let stop: Int = 0

    while stop == 0 {
        // ── ALWAYS-ON INTERRUPT CHECK (the cooperative cancellation point) ──
        let sig: String = token_check(tok)
        if str_eq(sig, "") {
            // no signal — proceed with one bounded step (or finish)
            if idx >= n {
                let status = "complete"
                let stop = 1
            } else {
                let step: String = json_array_get(steps, idx)
                let step_arg: String = "{\"goal\":\"" + json_escape_string(goal) +
                                       "\",\"idx\":" + int_to_str(idx) +
                                       ",\"step\":" + step + "}"
                let tid: Int = _swarm_spawn(step_fn, step_arg)
                let result: String = _swarm_join(tid)
                __channel_send(out_ch, result)
                let idx = idx + 1
                let completed = completed + 1
            }
        } else {
            // a signal arrived — ABSORB it immediately, before any more work
            let kind: String = json_get(sig, "sig")
            if str_eq(kind, "KILL") {
                // terminate cleanly — commit nothing further. Bounded purview =
                // no half-committed global state to unwind.
                __channel_send(out_ch, "[KILL absorbed @step " + int_to_str(idx) +
                                     " — stopped, no wasted continuation]")
                let status = "killed"
                let stop = 1
            } else {
                if str_eq(kind, "REDIRECT") {
                    // ABSORB the correction: re-plan onto the new goal/steps.
                    // Completed steps + emitted outputs are PRESERVED; only the
                    // remaining plan is replaced.
                    let kept: Int = completed
                    let goal = json_get(sig, "goal")
                    let steps = json_get_raw(sig, "steps")
                    let n = json_array_len(steps)
                    let idx = 0
                    let status = "redirected"
                    __channel_send(out_ch, "[REDIRECT absorbed @step " +
                                         int_to_str(kept) + " — kept " +
                                         int_to_str(kept) +
                                         " done, re-planned to goal=" + goal + "]")
                } else {
                    if str_eq(kind, "PAUSE") {
                        // hold state; idle-wait for the next signal
                        __channel_send(out_ch, "[PAUSE absorbed @step " +
                                             int_to_str(idx) + " — holding plan]")
                        let status = "paused"
                        let resumed: Int = 0
                        while resumed == 0 {
                            let s2: String = token_wait(tok)
                            let k2: String = json_get(s2, "sig")
                            if str_eq(k2, "RESUME") {
                                __channel_send(out_ch, "[RESUME @step " +
                                                     int_to_str(idx) + "]")
                                let status = "running"
                                let resumed = 1
                            } else {
                                if str_eq(k2, "KILL") {
                                    __channel_send(out_ch, "[KILL absorbed while paused]")
                                    let status = "killed"
                                    let stop = 1
                                    let resumed = 1
                                } else {
                                    if str_eq(k2, "REDIRECT") {
                                        let goal = json_get(s2, "goal")
                                        let steps = json_get_raw(s2, "steps")
                                        let n = json_array_len(steps)
                                        let idx = 0
                                        __channel_send(out_ch, "[REDIRECT absorbed while paused — goal=" + goal + "]")
                                        let status = "running"
                                        let resumed = 1
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    // final purview snapshot
    let final: String = "{\"id\":\"" + pid +
                        "\",\"goal\":\"" + json_escape_string(goal) +
                        "\",\"idx\":" + int_to_str(idx) +
                        ",\"completed\":" + int_to_str(completed) +
                        ",\"planned\":" + int_to_str(n) +
                        ",\"status\":\"" + status + "\"}"
    __channel_send(out_ch, "[DONE status=" + status +
                         " completed=" + int_to_str(completed) + "]")
    return final
}

// ── Coordinator convenience — dispatch an interruptible worker ─────────────────

// swarm_dispatch — spawn a natively-interruptible worker on a purview.
//   step_fn — name of the per-step executor (String)->String
//   purview — the meaning-plan (from purview_new)
// Returns a handle JSON: {"tok":T,"out":O,"tid":D} — the coordinator holds `tok`
// to interrupt/redirect/kill live, and drains `out` for results.
fn swarm_dispatch(step_fn: String, purview: String) -> String {
    let tok: Int = token_new()
    let out_ch: Int = __channel_new(0)
    let arg: String = "{\"tok\":" + int_to_str(tok) +
                      ",\"out\":" + int_to_str(out_ch) +
                      ",\"step_fn\":\"" + step_fn +
                      "\",\"purview\":" + purview + "}"
    let tid: Int = _swarm_spawn("swarm_worker_run", arg)
    return "{\"tok\":" + int_to_str(tok) +
           ",\"out\":" + int_to_str(out_ch) +
           ",\"tid\":" + int_to_str(tid) + "}"
}
