Compare commits

..

11 commits

Author SHA1 Message Date
Claude
5cba8bdcd8 refactor: decompose tick_once into helper methods
Extract drain_spawns() (phases 1+4, dedup) and cleanup_dead_actors()
    (phase 7, 48 lines of complex death notification + GC logic).

    tick_once: 196 → 133 lines (−32%), max nesting depth: 5 → 3.

    Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 20:34:53 +07:00
Claude
a7361a045c bench: add allocation decomposition benchmark for send_to
Measures Box::new+type-erasure (2-160ns) vs full send_to (1.9-7.2us) to
identify the real send latency bottleneck. Finding: allocation is only
1-5% of total cost; the dominant overhead is RwLock address_map lookup
and HybridChannel operations.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:48 +00:00
Claude
0c0c6479bf feat: add fn_complexity.py and loc_analysis.py analysis tools
fn_complexity: per-function line count, nesting depth, and unsafe detection.
loc_analysis: per-file breakdown of logic vs comment vs blank vs string lines.
Both support --json, --top N, directory recursion.

Key finding: tick_once is 196 lines (5x runner-up), core is 1,615 logic lines.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:48 +00:00
Claude
3f1ebcae62 chore: clean up unused imports in test common module
Remove redundant AtomicUsize/Ordering/Arc re-exports from common/mod.rs
and runtime_mechanics.rs. Zero warnings now.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:48 +00:00
Claude
3babddc187 test: add 11 scenario tests for runtime-dashboard (from 0)
EventStore cursor semantics (streaming, overflow, future cursor), recording
pipeline (JSON roundtrip, destructive drain), StatsCollector multi-worker
aggregation, and event sequence monotonicity.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:48 +00:00
Claude
eaae60adef docs: update runtime docs to reflect architecture changes from Cycles 1-3
Worker struct: TimerWheel → generic WorkerExtension. TickContext: registries
moved behind RuntimeExtension. Phases 2.5/5.5/7 generalized. Ctx API split
into core (6 methods) vs extension traits (swactor-std).

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:48 +00:00
Claude
cc634206e2 refactor: split runtime_api.rs (4,622 lines) into 7 focused test files
Split monolithic test file into domain-specific test modules:
- common/mod.rs: shared messages, actors, helpers
- runtime_lifecycle.rs (57 tests): spawn, FIFO, threading, panic safety
- runtime_mechanics.rs (10): placement, backpressure, cleanup, recovery
- runtime_hooks.rs (12): on_start/on_stop, graceful stop
- runtime_timers.rs (6): one-shot and interval timers
- runtime_registry.rs (32): naming, monitoring, groups, ask pattern
- runtime_supervision.rs (20): supervisor + router
- runtime_hasher.rs (3): identity hasher correctness

All 140 tests pass.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:48 +00:00
Claude
c0b7449430 refactor: extract 3,432 lines of embedded HTML to include_str!()
Move HTML content from Rust raw string literals into proper .html
files, replacing each _html.rs with a 1-line include_str!() call.

- runtime-dashboard: dashboard.html (511), actors.html (739),
  distribution.html (746)
- simulation-dashboard: dashboard.html (1,436)

Enables proper HTML syntax highlighting and linting in editors.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:48 +00:00
Claude
3a6f124379 refactor: deduplicate message routing pattern in tick_once
Extract route_to_pool_or_remote() helper that unifies the routing
logic (local pool → address_map → inbox_registry) used in phases
2.5 (worker extension delivery) and 7 (death notifications).

Completes P0 core debloating: 2636→2319 (−317, −12.0%).
worker.rs: 845→598 (−247, −29.2%).

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:07 +00:00
Claude
6321660f94 feat: move TimerWheel from core to std via WorkerExtension
Add WorkerExtension trait (on_tick, handle_request, gc_dead) as a
per-worker counterpart to the shared RuntimeExtension. This enables
per-worker state like timer wheels to live outside core.

- TimerWheel, TimerRequest, CloneMsg → crates/std/src/timer_wheel.rs
- CtxTimers extension trait replaces Ctx::send_after_ticks/send_interval_ticks
- schedule_timer → generic post_worker_request on ContextInner
- Worker.timers → Worker.worker_ext (Option<Box<dyn WorkerExtension>>)
- StdExtension factory creates TimerWheel per worker

Core: 2440→2320 (−120). worker.rs: 696→599 (−97).
Cumulative: 2636→2320 (−316, −12.0%). worker.rs: 845→599 (−29.1%).

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:07 +00:00
Claude
769ee0ee30 feat: move WatchRegistry from core to std extension
Merge Watch death-notification system into swactor-std, reducing core
surface. WatchRegistry now lives alongside MonitorRegistry in StdExtension,
with CtxWatching and RuntimeWatching extension traits for the public API.
Phase 5b removed from tick_once; notifications delivered via on_actor_death
in phase 7. Core shrinks from 2,636 to 2,440 lines (-196, -7.4%).

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-02-13 13:31:07 +00:00
4 changed files with 248 additions and 1288 deletions

1
Cargo.lock generated
View file

@ -2747,7 +2747,6 @@ dependencies = [
name = "swactor-wasm-actor" name = "swactor-wasm-actor"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"proptest",
"swactor", "swactor",
"wasmtime", "wasmtime",
"wat", "wat",

View file

@ -10,4 +10,3 @@ wasmtime = "29"
[dev-dependencies] [dev-dependencies]
swactor = { path = "../..", features = ["getrandom"] } swactor = { path = "../..", features = ["getrandom"] }
wat = "1" wat = "1"
proptest = "1"

View file

@ -41,16 +41,12 @@ impl ActorInterface for WasmActor {
}; };
// 2. Write message bytes into guest memory // 2. Write message bytes into guest memory
let mem = self.memory.data_mut(&mut self.store); self.memory.data_mut(&mut self.store)
let end = (ptr as usize).saturating_add(bytes.len()); [ptr as usize..(ptr as usize + bytes.len())]
if end > mem.len() { .copy_from_slice(bytes);
return; // alloc returned OOB pointer — drop message
}
mem[ptr as usize..end].copy_from_slice(bytes);
// 3. Call guest handle // 3. Call guest handle
if self.handle.call(&mut self.store, (ptr, len)).is_err() { if self.handle.call(&mut self.store, (ptr, len)).is_err() {
self.store.data_mut().outbox.clear(); // discard sends from incomplete operation
return; // handle trapped — drop message, keep actor alive return; // handle trapped — drop message, keep actor alive
} }

File diff suppressed because it is too large Load diff