Recon
Recon: a running program's view of itself, from March code.
The same numbers the observe socket serves (forge observe, forge top), read in-process through the observe_query builtin, which answers one request line with the socket's JSON envelope. Observe tier: every function takes a Cap(Actor.Introspect) (mint it with Actor.introspect(io) in main) and none returns a crash message, only how an actor died.
Backends. Compiled, the answers come from the runtime's actor table and counters. Interpreted, from the interpreter's actor table: mailboxes are usually empty (delivery is eager), there is one scheduler, and no memory gauges, so those fields read 0 or None.
Windows. proc_window and scheduler_usage take two samples window_ms apart and sleep THIS green thread in between (the runtime's own windowed verbs would sleep a scheduler's OS thread, so in-process callers get them refused).
R3 of specs/plans/2026-09-28-observe-recon-shell-plan.md.
Types
Functions
Every live actor, deepest mailbox first.
The last n crashes (at most 256), newest first, without their messages.
The current hot-reload epoch and the pins held at each epoch.
rows (from top) as a table, one line per actor and a header line, in the order given. The ranked column is headed VALUE.
The live actor with pid index pid (what pid_to_int returns), or None when it is dead or was never spawned.
match Recon.info(c, 3) do
Some(a) -> println(a.type_name ++ " " ++ int_to_string(a.mbox))
None -> println("gone")
endActors, queued messages, memory and crash totals from one snapshot.
Print top(c, attr, n) as a table: the one-liner for the remote shell, Recon.print_top(intro, "stack", 10).
The n live actors highest on attr, as (pid, value), highest first. attr is one of "mbox" (queued plus held), "crashes" (its own and its children's crashes in the last hour), "slices", "msgs_in" or "msgs_out" (cumulative since spawn; see proc_window for a rate). An unknown attr gives Nil.
Like proc_count for "slices", "msgs_in" or "msgs_out", but ranked by how much each rose over the next window_ms: two samples, with this green thread asleep between them. An actor born during the window counts from 0.
Each scheduler's utilisation over the next window_ms, as (scheduler id, fraction busy in 0.0 to 1.0): two samples of its idle time, with this green thread asleep between them. Nil under the interpreter (no scheduler threads).
The source text of the version of name this node runs now, as it stood when that version was built and uploaded: the declaration, with the comments up to the next one. Debug tier: it needs Cap(Actor.Debug) and a $MARCH_DEBUG_POLICY file that lists SOURCE, else Err("policy"). Err("no_source") when the text of this version was never uploaded (forge deploy hot uploads it; a node built elsewhere has only its own).
match Recon.source(debug, "Billing.total") do
Ok(text) -> println(text)
Err(e) -> println("no source: " ++ e)
endThe n live actors highest on attr, biggest first, each with its supervision. Equal values are ordered by pid, so the same node state gives the same list.
`attr` is "mbox" (or "mailbox"; waiting work: queued plus held), "stack" (committed machine-stack
bytes, the memory the runtime can attribute to one actor), "crashes" (its own and its
children's in the last hour), or "slices", "msgs_in", "msgs_out" (cumulative since
spawn; see `proc_window` for a rate). An unknown `attr` gives Nil. Heap bytes owned by
an actor are not tracked, so there is no "heap" ranking.
Recon.top(c, "mbox", 10)The supervision tree: one node per root (an actor with no supervisor that supervises or spawned something live), children nested. Actors in neither role are left out; actors lists everything.
Which version of the reloadable function name ("Mod.fn", or an actor's "Counter_dispatch") this node runs: its impl_hash, the baseline it was built with, and when the current version was activated. Err("not_found") when name is not a reload slot of this node (a typo, a function the build does not make reloadable, or an interpreted program).
match Recon.which(intro, "Billing.total") do
Ok(v) -> println(v.impl_hash)
Err(e) -> println(e)
end