runa/docs/architecture.md

37 KiB

Runa — Project Specification and Architecture

Status: Architecture specification, revision 4 (Runa naming)
Primary host language: Rust
Initial embedded language: Rune
Inspirations: Smalltalk/Squeak/Pharo, Morphic, Erlang/OTP
Primary interfaces: Self-contained graphical world and VS Code extension connected to the same live image through a network API

1. Vision

Build a self-contained, persistent, live programming world. Its fundamental computational entities are actors, not shared mutable objects. The environment itself is programmable and inspectable, with live actor spawning, message sending, state inspection, behavior editing, debugging, and hot code upgrades. A VS Code extension provides an equally capable external interface to the same running image via a programmatic API.

Rust supplies a minimal, reliable substrate; increasingly much of the actor framework, graphical world, and development tools should be implemented in Rune. The end state is a Smalltalk-like image with Erlang-inspired actor isolation and modern tooling.

Core goals

  • Inspect, spawn, stop, restart, and supervise actors from both graphical world and VS Code.
  • Inspect state, mailboxes, behavior versions, failures, and execution status.
  • Send messages and evaluate development operations interactively.
  • Edit image source files, commit and roll back their history, and reload or become another script without restarting the image.
  • Support interruptible execution, pause/resume, and eventually source-level stepping.
  • Preserve stable actor identity and optionally mailbox contents across actor incarnation failures.
  • Store canonical Rune source and immutable implementations by content identity; cache compilation.
  • Build self-inspecting, self-modifiable graphical tools, gradually moving implementation from Rust to Rune.
  • Persist and restore meaningful parts of the image without assuming arbitrary stack serialization.

2. Architectural Principles

  1. One authoritative live image. In-world tools and VS Code are clients of the same semantic development services.
  2. Actor isolation. No direct mutable cross-actor state sharing. All normal interaction occurs through messages.
  3. Separate identity, mailbox, state, handler, and incarnation. A failed handler execution need not destroy an actor's identity or mailbox.
  4. Stateless handlers. Actor-specific mutable state is explicit input/output, not hidden global state.
  5. Immutable code versions. A behavior name points to an exact implementation; active executions retain their own version.
  6. Rust mechanisms, Rune policies. Native kernel for scheduling, execution control, memory/resource ownership, platform I/O; Rune for behaviors, UI, supervision policy, and tools where practical.
  7. Safe-point upgrades and inspection. Never unsafely mutate a currently executing handler.
  8. Honest guarantees. Distinguish cooperative yielding from true preemption, in-memory retention from disk durability, and at-least-once delivery from exactly-once effects.
  9. Replaceable language integration. Keep a narrow runtime adapter, but do not over-generalize before Rune feasibility is tested.

3. Layered Architecture

+-----------------------------------------------------------+
| Live graphical world (Morphic-inspired)                   |
| Windows, widgets, actor browser, inspector, debugger      |
+-------------------------------+---------------------------+
| Rune development services     | VS Code extension         |
| Behavior registry, workspace  | Virtual docs, trees, DAP  |
+-------------------------------+---------------------------+
| Unified image API / development protocol                  |
| Actors, mailboxes, state, code, debugging, events          |
+-----------------------------------------------------------+
| Rune actor framework                                      |
| Stateless handlers, supervision policies, UI behaviors    |
+-----------------------------------------------------------+
| Rune runtime adapter / compilation and execution          |
+-----------------------------------------------------------+
| Rust actor kernel                                         |
| Identity, mailboxes, scheduler, transactions, monitoring  |
+-----------------------------------------------------------+
| Rust platform substrate                                   |
| Graphics, input, timers, filesystem, networking, storage  |
+-----------------------------------------------------------+

The graphical world should use internal service calls to the image API. The VS Code extension, CLI tools, tests, and local automation connect to the same live image through the versioned runa-debug WebSocket transport; trusted embedded callers can use in-process calls. The initial WebSocket transport is unauthenticated and must bind only to loopback; a remote-capable network boundary must authenticate and authorize clients. Client connections do not create a separate actor runtime or image.

4. Fundamental Actor Model

An actor comprises distinct components:

Actor {
    actor_ref: ActorRef,           // opaque public reference
    mailbox: MailboxId,           // independent queue; may survive restart
    state: StateRecord,           // committed private state
    behavior: ImplementationId,   // immutable handler implementation
    incarnation: Generation,      // changes on restart
    status: ActorStatus,
    supervisor: Option<ActorRef>,
}

4.1 Identity

Actor references are opaque, never raw pointers or sequential numbers. The prototype uses 21-character URL-safe NanoIDs for public references. A logical actor retains its reference and mailbox across restart but receives a new incarnation. Random references are not an authentication or authorization mechanism; network access remains loopback-only until access control exists. Distinguish runtime-instance identity from actor identity if remote or persisted references are introduced.

4.2 Mailbox

The mailbox is managed independently of handler execution. Messages have at least queued, reserved, acknowledged, and dead-lettered lifecycle states where durable processing is enabled. Define ordering explicitly; FIFO within a queue does not imply deterministic ordering between concurrent senders.

Start with bounded queues and observable backpressure. For durable mailboxes, initially target at-least-once processing, retry limits, and dead-letter handling. Retention across actor restarts does not imply survival across host process crashes; disk durability requires explicit storage and recovery.

4.3 State

State is owned by the actor runtime, logically private, and supplied to each scheduled handler invocation. The handler works with a controlled draft or value and optionally returns new state; it cannot mutate committed state directly. Returning unit or omitting a return retains the previous committed state and discards draft mutations. State has a schema/version where useful and can be inspected via snapshots at safe points.

4.4 Stateless handler

The handler is a stateless Rune function associated with an immutable implementation ID. Conceptually:

handle(ctx, state, sender, message) -> new_state
on_shutdown(state) -> new_state // optional

ctx.send(recipient, data) records an ordinary send with the calling actor as sender. ctx.send_from(sender, recipient, data) supports forwarding. Sender metadata lives in a runtime envelope and is passed separately from data; host-originated messages have unit sender. Actor-specific state must not be stored in globals or implicit mutable closures. These handler and send APIs are implemented against Rune 0.14.2; the handler may await approved operations.

4.5 Effects

Context sends buffer owned effects instead of delivering immediately. State and all validated sends commit together; later mutation of a Rune value does not change a buffered message. Timers and durable external requests remain planned. System lifecycle messages are a separate host-only control path: actors cannot construct stop, kill, restart, pause, resume, reload, or become commands through ordinary sends. Stop completes the current handler then invokes optional on_shutdown(state) before stopping; kill skips shutdown. Restart retains the loaded script and state. Reload/become switch immutable implementations only after an active handler completes.

ctx.send(recipient, data);
new_state

On durable paths, commit the state transition, input-message acknowledgment, and transactional outbox records atomically. Delivery of outbox effects occurs afterward and may be retried. Exactly-once external side effects are not guaranteed; use idempotency keys and deduplication where needed.

5. Scheduling, Awaiting, and Interruption

Each actor processes at most one active handler invocation at a time, including suspended invocations, unless a future explicitly designed concurrency policy says otherwise. Other messages remain queued.

A scheduled invocation receives its committed state, a reserved message, the selected implementation, and execution context. It runs until it:

  1. Completes, producing a transition to commit.
  2. Awaits an asynchronous operation, suspending without blocking a native worker thread.
  3. Is paused/interrupted at a supported safe point.
  4. Fails or is cancelled.
  5. Reaches a configured execution budget.

Await is not commit. State and effects from an incomplete invocation remain uncommitted. On crash, the current invocation is discarded or recovered according to documented policy, and its reserved message can be retried. Long awaits can consume resources and complicate recovery; establish limits and cancellation rules.

5.1 Interruptibility requirement

Interruptible Rune execution is a foundational requirement, not a future nice-to-have. The runtime should eventually support pause, resume, cancel, execution budgets, runaway-code detection, breakpoint locations, and source-level stepping.

Phase 0 must verify actual Rune APIs and limitations: cooperative yield, VM instruction hooks/budgets, native callback blocking, async suspension, frame inspection, and safe interruption of CPU-bound loops. If Rune lacks the necessary mechanisms, evaluate bytecode instrumentation, interpreter modifications, or a controlled fork. Do not claim preemptive scheduling until it is actually implemented and tested. Native Rust callbacks must also define their cancellation behavior.

5.2 Failure and supervision

Separate handler exceptions, invocation cancellation, actor incarnation failure, supervisor restarts, host process failure, and machine failure. Supervisors can restart an actor with retained mailbox, last committed state, and the same or a compatible upgraded handler. Supervision strategies (one-for-one, one-for-all, rest-for-one) can initially live in Rune, atop native monitoring and lifecycle events. Add restart-intensity limits and poison-message handling.

6. Content-Addressed Rune Code

Keep four distinct things:

  1. Original source for editing, diagnostics, and exact text preservation.
  2. Canonical source normalized deterministically and semantics-preservingly.
  3. Source identity derived from canonical bytes with a versioned hash scheme.
  4. Compiled artifact cached using all compilation inputs.

Use a domain-separated hash such as BLAKE3 and a canonicalization version. Never strip arbitrary whitespace/comments without parser-aware analysis; preserve original source and source maps for debugging.

Example identifiers:

rune-source:v1:blake3:<digest>
compiled:<digest-of-source-plus-toolchain-configuration>

A compiled artifact cache key must include source identity, Rune compiler version, compiler settings, enabled native modules, runtime ABI, relevant features, and target constraints. Do not assume artifacts are portable between Rune versions or host configurations.

Mutable image files are organized beneath a source directory:

source/actors/counter.rn -> current working source
actors/counter -> commit history [readable-generated-revision, ...]

Spawn takes actors/counter and compiles the current working source, even uncommitted. Commit snapshots are version control only and can retain invalid source. Rollback restores working source, not a running actor's frames. Saves use expected source identities to prevent conflicting editor updates. Active frames retain exact immutable implementations independently of source editing and commit history. Garbage collection must respect those references.

6.1 Hot upgrade procedure

  1. Save working source with an expected source identity; commit separately if desired.
  2. Reload the current actor path or become another path; hash and compile current text (or reuse cache).
  3. Report diagnostics without changing the running behavior on failure.
  4. Register an immutable implementation.
  5. Validate state compatibility or execute explicit migration.
  6. Install at a safe boundary (initially between message handlers).
  7. Emit change events. Source commit history remains independent.

Do not rewrite suspended execution frames in place.

7. Graphical World

Build a self-contained Morphic-inspired desktop where windows, widgets, and development tools are themselves inspectable and eventually written primarily in Rune.

Required in-world operations: browse actors and behaviors; spawn, stop, restart, pause, and resume actors; inspect state, mailboxes, failures, supervisor relationships, and versions; send messages; edit and compile behavior source; install/rollback versions; inspect execution; view events and diagnostics.

Not every graphical node must be an actor. Use actors for independently scheduled components and ordinary Rune data for geometry, style, and lightweight scene nodes. UI actors update a retained scene representation; a scene manager produces immutable render snapshots consumed by native graphics code. Avoid synchronous arbitrary actor callbacks on the render thread. Native code initially owns windowing, drawing, and input capture; Rune progressively owns composition, layout, event routing, widgets, and tools.

The graphical inspector should support selecting a component, locating its owning actor, inspecting its state and behavior, editing its implementation, and observing live changes. The development environment must be able to inspect itself.

8. Unified Programmatic Image API

The running image exposes a versioned semantic API, used by graphical tools, CLI tools, tests, automation, coding agents, and the VS Code extension. External clients use WebSockets to reach the same running image; there is no stdio transport. Client location does not change actor placement or message-passing semantics.

Suggested operation families (provisional names):

image.info            image.snapshot       image.restore
image.subscribe

actor.list            actor.spawn          actor.inspect
actor.send            actor.call           actor.pause
actor.resume          actor.stop           actor.restart
actor.subscribe

mailbox.inspect       mailbox.peek         mailbox.statistics
mailbox.retry         mailbox.deadLetters

state.inspect         state.snapshot       state.history

behavior.list         behavior.get         behavior.compile
source.write          source.commit        source.rollback
behavior.history

code.getSource        code.getArtifact     code.getMetadata
code.listReferences

debug.attach          debug.detach         debug.pause
debug.resume          debug.step           debug.breakpoints
debug.stack           debug.inspectFrame

These are desired capabilities, not a requirement to implement every endpoint immediately. Define explicit request/response schemas, error codes, authorization, optimistic revision checks, and event subscription semantics. Mutations should return structured outcomes and emit events.

8.1 Security

Normal actors should not automatically receive development privileges. Arbitrary evaluation, state mutation, code replacement, and process control require explicit capabilities. The initial WebSocket bridge accepts unauthenticated connections only on IPv4 or IPv6 loopback and grants each connection the full development API; any local process may connect. It is suitable only for trusted local development. Never bind this endpoint to a non-loopback address or expose it remotely until authentication and authorization are implemented.

9. VS Code Extension

VS Code is an external client of the same live image, not a separate runtime. The initial development transport is versioned JSON over runa-debug WebSockets, bound to loopback and unauthenticated; the extension must connect only to a trusted local image. Remote connections remain out of scope until authentication and authorization are implemented. The extension does not host actors or create a separate image. The REPL uses the same WebSocket protocol.

The first extension increment implements explicit connect/disconnect, an actor explorer, and read-only state/mailbox/execution snapshots. Server image_changed notifications automatically refresh the tree and open documents without scheduler ticks or event drains. Revisions and refreshes are coalesced rather than replayed, and changes during a fetch trigger a follow-up refresh. Cached open snapshots are marked disconnected on connection loss and isolated by connection session so reconnecting to another image cannot overwrite them. Spawn with explicit source path/state, structured send, system lifecycle commands, and explicit scheduler ticks are implemented. The extension mounts an editable source directory with nested Rune files and file-context spawn actions. Saving writes working source; committing and browsing history are separate commands. Commit source documents are read-only and isolated by connection session. Image notifications refresh listings without replacing historical source text. Remaining UX below is planned; detailed runtime event subscriptions and durable replay still need design.

Required extension UX:

  • Connection management and runtime status.
  • Actor tree with spawn, stop, restart, pause, and resume actions.
  • Actor state and mailbox inspector, including message sending.
  • Directory-based image source navigation.
  • Editable image source files with independent commit/history/rollback actions.
  • Read-only virtual documents for exact content-addressed implementations.
  • Compilation diagnostics, update conflicts, and runtime event stream.
  • Later: Debug Adapter Protocol integration for breakpoints, frames, and stepping.

Suggested virtual URIs:

source/actors/counter.rn
source-history:/<session>/actors/counter/<revision>.rn

Saving the working file does not compile, commit, or install it. Spawn compiles current content; reload/become install at a handler boundary. The historical document is immutable and read-only. Avoid silently overwriting another client's working source.

10. Persistence

Implement persistence incrementally:

  1. Source: canonical/original source, immutable implementations, registry revisions.
  2. Actor state: selected actor identities, committed state, handler references, supervision metadata.
  3. Environment: windows, layouts, workspaces, and development session state.
  4. Advanced: coordinated actor/message snapshots and, only if technically feasible, suspended execution state.

Start with SQLite or another simple durable store plus Git-friendly source export. Define snapshot consistency explicitly. Never assume arbitrary Rune stacks, closures, and native resources can be serialized and restored. Preserve mailbox and state transaction invariants across recovery.

11. Workspace Boundaries and Dependency Direction

The Rust workspace uses acyclic, inward-pointing dependencies. The executable (runa) is the composition root. Higher-level services depend on lower-level contracts; low-level crates never import graphical, transport, or application code.

11.1 Dependency diagram

Arrows mean "depends on", not runtime data flow:

               runa-world    runa       runa-cli
                    |        /   \         |
                    v       v     v        v
              runa-image-api      runa-protocol
                    |                  |
                    v                  |
                runa-runtime           |
                /        \             |
               v          v            |
          runa-actor   runa-rune       |
               \          /            |
                v        v             v
                 runa-core <-----------+

runa-world also depends on runa-graphics (platform drawing/input
contracts); runa-graphics depends only on runa-core when needed.
runa depends on runa-image-api and runa-protocol.
runa-cli uses runa-client and runa-protocol to connect to the image.
World, storage and platform integration remain planned.

The diagram is conceptual; Cargo dependencies should be kept minimal. A dependency is permitted only when the importing crate actually uses that crate's public contracts. The VS Code extension is a separate TypeScript project and depends on the wire protocol, not Rust crates.

11.2 Crate ownership

Crate Owns May depend on
runa-core Small, stable value types shared across independent layers: actor references, source and implementation IDs, portable actor values, common error identifiers Minimal external libraries only
runa-code Original source retention, versioned source/implementation hashing, compile-input fingerprints, in-memory compiled artifact cache runa-core, runa-rune, BLAKE3
runa-actor Mailbox and state-transition semantics, message reservation, acknowledgment, effects, actor lifecycle definitions runa-core
runa-rune Rune compilation, VM execution, suspension, interruption hooks, language-specific diagnostics runa-core, Rune libraries
runa-runtime Scheduling, actor execution coordination, state commits, crash recovery, binding actor semantics to Rune execution runa-core, runa-actor, runa-code, runa-rune
runa-image-api Trusted in-process application services for spawning, inspecting, sending, editing, and upgrading runa-core, runa-actor, runa-code, runa-runtime
runa-protocol Versioned transport-neutral request/response/event DTOs and validation serde
runa Image startup, configuration, protocol dispatch, and loopback-only WebSocket transport runa-protocol, runa-image-api
runa-cli Interactive REPL and connection argument parsing runa-client, runa-protocol
runa-client Loopback runa-debug WebSocket client runa-protocol
runa-graphics Native window, rendering and input mechanisms; no actor policy runa-core if necessary
runa-world Morphic-style graphical composition and in-world development UI runa-image-api, runa-graphics

The current prototype has added runa-code for exact-source identity and in-memory compilation caching, and runa-image-api as the trusted, in-process application-service boundary used by the local CLI. It deliberately does not canonicalize source yet; canonicalization needs a semantics-preserving, source-map-aware policy. The image API is not yet a network API and performs no authentication or authorization; callers must be trusted by the host.

The initial runa-image-api provides process-local behavior source retrieval, publication, history and rollback, actor lifecycle and messaging, snapshots, ticks, and event retrieval. The CLI uses these application services rather than reaching into the code store and runtime directly. runa-protocol now defines versioned, serializable request, response, and event DTOs without depending on a transport or the runtime implementation. runa now dispatches those commands through the image API and maps service results into protocol replies and typed errors. The dispatcher is exposed through an unauthenticated WebSocket listener restricted to IPv4 or IPv6 loopback, enforcing a 1 MiB request limit. The WebSocket listener shares one image across at most 16 concurrent clients. Every local client receives full development API access, so the endpoint is for trusted local development only. In-process consumers call runa-image-api, and external local tools can use runa-client to connect to the server. Actors do not depend on network transport to coexist in one live image. Remote network access will require authentication and authorization.

Important: runa-core must not contain schedulers, mailbox implementations, persistence engines, Rune VM types, service traits used by only one crate, generic helper collections, or GUI abstractions. Prefer a type defined in its owning crate until two independent consumers demonstrably need a stable shared representation.

11.3 Runtime interfaces and avoiding cycles

  • runa-actor defines actor rules and data contracts; it does not spawn threads or call Rune.
  • runa-rune executes Rune code but does not own actor scheduling, mailboxes, or image APIs.
  • runa-runtime coordinates both. Initially it can depend directly on runa-rune; introduce an execution-adapter trait only when a second implementation or test double justifies it. Place the trait at the lowest layer that actually consumes it, rather than automatically in runa-core.
  • runa-image-api is the single application-service entry point for in-world tools and external clients. It centralizes lifecycle invariants and revision checks; host-level permissions and authentication remain required at any external boundary.
  • runa-protocol holds wire DTOs, not runtime service implementations. Translate DTOs at the server boundary; never serialize internal actor locks or VM references.
  • runa-world uses the same runa-image-api operations as the external bridge. It does not reach into runtime internals for convenience.
  • Native platform handles stay behind runa-graphics (or a later platform crate), not in runa-core.

For content-addressed code, runa-core owns stable source/implementation ID value types, and runa-code owns source hashing, compile fingerprints, storage, and compilation caching. The current prototype hashes exact source bytes without canonicalization and keeps the cache in memory.

For persistence, begin with a concrete adapter wired by runa. Extract runa-storage only when its durable mailbox, state, outbox and artifact contracts are clear. Keep database-specific details out of actor semantics and out of runa-core.

11.4 Repository layout

runa/
├── Cargo.toml
├── README.md
├── docs/
│   ├── architecture.md
│   ├── actor-model.md
│   ├── runtime.md
│   ├── protocol.md
│   └── decisions/
├── crates/
│   ├── runa-core/
│   ├── runa-actor/
│   ├── runa-code/
│   ├── runa-rune/
│   ├── runa-runtime/
│   ├── runa-image-api/
│   ├── runa-protocol/
│   ├── runa-cli/
│   ├── runa-client/
│   ├── runa-graphics/
│   ├── runa-world/
│   └── runa/
├── runa-std/
│   ├── core/
│   ├── actors/
│   ├── ui/
│   └── tools/
├── scripts/
├── vscode/
├── examples/
└── tests/

This is a target layout, not a request to generate empty crates. For Phase 0, begin with runa-rune and a small executable test harness. Introduce runa-core, runa-actor, and runa-runtime only as the corresponding functionality is implemented. Enforce the dependency direction through Cargo manifests and periodic dependency-graph checks; reject circular dependencies and upward imports in code review.

12. Implementation Roadmap

Phase 0 — Rune feasibility (first priority)

  • Embed Rune in a Rust workspace and compile/run a minimal script.
  • Register a native Rust function and call it from Rune.
  • Demonstrate async suspension and resumption using verified APIs.
  • Investigate interruption of CPU-bound code, execution budgets, frame inspection, and debugging hooks.
  • Measure VM/context memory overhead and compiled-code sharing.
  • Test cross-thread safety and runtime isolation assumptions.
  • Document actual Rune limitations and potential mitigation strategies.

Exit criterion: Evidence-based choice of execution model and a running example. Do not start GUI implementation until this is assessed.

Phase 1 — Minimal actor kernel

  • Stable IDs, incarnation generations, bounded mailboxes, state records, stateless handlers.
  • Spawn/send/receive/terminate, sequential scheduling, basic lifecycle and error events.
  • Demonstrate two Rune actors exchanging structured messages without shared mutable state.

Phase 2 — Transactional processing

  • Reserve/ack/retry messages; commit state transitions.
  • Explicit effects, error handling, and eventually an outbox for durable sends.
  • Test crash/restart retaining mailbox and last committed state.

Phase 3 — Content-addressed code

  • Original/canonical source storage, hashing, cache keys, behavior registry, revision history.
  • Compile or retrieve cached artifacts; retain exact implementation IDs.

Phase 4 — Live code and debugging foundation

  • Safe-point behavior upgrades, invalid-source rollback, state migration policy.
  • Pause/resume/cancel and initial execution inspection, subject to Rune feasibility.

The current runtime prototype supports implementation installation only at an idle handler boundary, cooperative pause/resume between scheduler ticks, and an explicit host-provided migration closure for structured actor state. Actor snapshots expose active VM instruction progress, instruction pointer, and call-frame depth. State and messages are owned portable trees of null, booleans, integers, strings, lists, records, and distinct actor references. Rune handlers receive (ctx, state, sender, message) and optionally return state; unit returns retain committed state and discard invocation-local draft mutations. context sends are buffered and committed atomically with state. Rune heap objects never become shared committed actor data. State schemas, version compatibility checks, Rune-authored migration functions, source mapping, and local-variable inspection remain future work.

Phase 5 — Image API

  • Headless server, versioned protocol, actor and mailbox operations, source editing, events.
  • CLI client that can spawn, inspect, send messages, and replace behaviors.

The first Phase 5 increments are a trusted in-process application API used by the CLI, a transport-neutral versioned protocol contract, and a dispatcher exposed through bounded loopback WebSockets. The interactive runa-cli uses runa-client to connect over loopback WebSockets to a server-owned image, rather than embedding a runtime. Multiple clients share actors, working source, and commit history; client disconnect/reconnect leaves the running image intact. The runa server provides an unauthenticated runa-debug WebSocket endpoint restricted to loopback for local VS Code development. Remote access will require authentication and authorization; neither network transport nor client location changes the single-image actor model.

Both binaries use Clap derive/env argument parsing. runa starts the server at 127.0.0.1:3737 by default (--listen / RUNA_LISTEN), and runa-cli connects to that address (--connect / RUNA_CONNECT). Flags override environment variables. Mise's start and cli tasks run the server and REPL respectively.

Phase 6 — Graphical world

  • Native window/rendering, input, scene manager, Rune-driven button and window.
  • In-world actor browser, state inspector, behavior editor, and message sender.

Phase 7 — VS Code extension

  • Connection, actor explorer, state/mailbox inspection, spawn/send controls.
  • Virtual behavior documents, compilation diagnostics, hot upgrades, runtime events.

Connection management, the actor explorer, and read-only snapshot inspection are implemented in vscode/, along with spawn/send, lifecycle controls, and explicit scheduler ticks, editable image source files, commit history, and immutable revision documents. Reload/become install scripts at safe boundaries. Structured compilation diagnostics and detailed runtime event subscriptions remain planned. Non-destructive image-change notifications and automatic snapshot refresh are implemented over the shared runa-debug WebSocket transport.

Phase 8 — Persistence and advanced debugger

  • Durable mailboxes and state, snapshot/restore, supervised recovery.
  • Source-level debugging and richer interruption/stepping as supported by runtime.

13. Critical Risks and Open Decisions

Topic Risk / decision
Rune interruption Verify whether CPU-bound execution can be paused or budgeted safely.
Async invocation Define how awaits preserve private state and what is retried after crash.
VM isolation Determine per-actor VM vs shared compiled code and isolated execution contexts.
Scheduler fairness Cooperative yield alone does not prevent a runaway CPU-bound handler.
Effects Commit/retry semantics and idempotency for external side effects.
Mailbox durability In-memory restart retention vs process-crash durable storage.
Source normalization Canonicalization must preserve semantics and debugging locations.
Code upgrade Active frames must retain exact old implementation versions.
UI performance Avoid an actor per tiny graphical node and render-thread actor calls.
Persistence Full execution-stack image restoration may require interpreter changes.
API security Debugging and code installation are privileged operations.

14. Initial Non-Goals

No full BEAM clone, distributed actors, millions-of-actors guarantee, full Morphic compatibility, custom Rune compiler, complete IDE, full stack serialization, production-grade hostile-code sandbox, or complete source-level debugger in the first prototype. Do not build a large abstraction framework before the Rune experiment validates it.

15. Instructions for a Local Coding LLM

Act as the principal systems architect and implementation partner. Work incrementally. Begin only with Phase 0. Verify exact Rune APIs and syntax against the pinned version; never invent working APIs. Distinguish confirmed capabilities from hypotheses. Produce a minimal Cargo workspace, runnable examples, tests where practical, build/run instructions, measurements, and a concise feasibility report. Record architectural decisions and unresolved risks. Do not silently change the mailbox/state/handler decomposition, collapse development tools into separate runtimes, or bypass actor isolation for inspection.

First task: Implement a minimal Rust + Rune experiment that executes a Rune function, invokes a registered native Rust function, demonstrates supported asynchronous suspension, and investigates interruption and compiled-code reuse. Stop after reporting the results and proposed Phase 1 architecture.

16. Long-Term Acceptance Scenario

A developer launches the graphical world, spawns actors/counter, sends it a message, inspects its mailbox and committed state, edits source/actors/counter.rn, optionally commits a source snapshot, then reloads the actor at a handler boundary and sees subsequent messages use the new script. New spawns use working source regardless of commit status. The same operations are possible through VS Code using the image API. A failed incarnation can be restarted with the same identity and retained mailbox, and the world can eventually save and restore a meaningful development session.

North star: The world is the program; actors are its living components; development is continuous interaction with that world rather than a separate edit-build-run cycle.