FS7b: lifecycle + arena meta events from the freestanding actor, via Meta.ck #49

Manually merged
dferreira merged 6 commits from feat/049-fs7b-lifecycle-arena-meta-events-from-the-free into master 2026-03-03 14:07:20 +01:00
Owner

FS7b: lifecycle + arena meta events from the freestanding actor, via Meta.ck

Along the way:

  • decisions: scheduler/execution model — cooperative yields, one FIFO writer queue (owner, 2026-07-02)
  • decisions(scheduler): parallelism model — one-writer is per-STATE; scale out actors, round-robin
  • docs: horizon goals stated — event loop framing, servers < 64 KB in L1, CUDA lowering
  • FS-test: Test.ck + Assert.ck — ckc test compiles a FREESTANDING test binary; the divergent test path dies for the live stdlib
  • FS8: delete ck_rt — CkLang runs itself; Rust only compiles; one link mode

Touches 117 files: current-state.md, decisions-scheduler-model.md, docs/impl/runtime-testing/T9-bounded-arena-stress-reconcile.md, lowering.md, plan.md, scripts/deploy.sh and 111 more.

FS7b: lifecycle + arena meta events from the freestanding actor, via Meta.ck Along the way: - decisions: scheduler/execution model — cooperative yields, one FIFO writer queue (owner, 2026-07-02) - decisions(scheduler): parallelism model — one-writer is per-STATE; scale out actors, round-robin - docs: horizon goals stated — event loop framing, servers < 64 KB in L1, CUDA lowering - FS-test: Test.ck + Assert.ck — ckc test compiles a FREESTANDING test binary; the divergent test path dies for the live stdlib - FS8: delete ck_rt — CkLang runs itself; Rust only compiles; one link mode Touches 117 files: `current-state.md`, `decisions-scheduler-model.md`, `docs/impl/runtime-testing/T9-bounded-arena-stress-reconcile.md`, `lowering.md`, `plan.md`, `scripts/deploy.sh` and 111 more.
Author
Owner

Split. The decision in soft_asserts_run_freestanding_with_a_libc_identical_stream is a pure function now, with its own tests in src/rust/crates/ck_driver/tests/freestanding_test_run.rs.

Split. The decision in `soft_asserts_run_freestanding_with_a_libc_identical_stream` is a pure function now, with its own tests in `src/rust/crates/ck_driver/tests/freestanding_test_run.rs`.
Author
Owner

Unreachable today, which is not the same thing. crashed returns an error variant now, and src/rust/crates/ck_driver/tests/freestanding_actor_events_run.rs has a test that drives the path.

Unreachable today, which is not the same thing. `crashed` returns an error variant now, and `src/rust/crates/ck_driver/tests/freestanding_actor_events_run.rs` has a test that drives the path.
Author
Owner

Nothing outside calls armed. It is pub(crate) in src/rust/crates/ck_lower/src/lower/test_facts.rs now.

Nothing outside calls `armed`. It is pub(crate) in `src/rust/crates/ck_lower/src/lower/test_facts.rs` now.
dferreira approved these changes 2026-03-03 13:00:00 +01:00
dferreira left a comment

Happy with this. I ran the suite against the branch and it is green, src/rust/crates/ck_driver/tests/freestanding_test_run.rs included.

Happy with this. I ran the suite against the branch and it is green, `src/rust/crates/ck_driver/tests/freestanding_test_run.rs` included.
@ -0,0 +211,4 @@
site,
} = crashed
else {
panic!("event 3 must be ActorCrashed, got {crashed:?}");
Owner

This panics. Is crashed unreachable, or unreachable today? If it is the second I would rather see an error the caller can handle.

This panics. Is `crashed` unreachable, or unreachable today? If it is the second I would rather see an error the caller can handle.
@ -0,0 +54,4 @@
/// whose stream is event-identical to the libc run's (file id normalized),
/// with the intentional failing direction preserved (suite exit 1).
#[test]
fn soft_asserts_run_freestanding_with_a_libc_identical_stream() {
Owner

soft_asserts_run_freestanding_with_a_libc_identical_stream reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?

`soft_asserts_run_freestanding_with_a_libc_identical_stream` reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?
@ -0,0 +67,4 @@
/// An armed table: the six located special-form `FnId`s, as
/// `(append_str, str_len)` + `(arm, head, entries, disarm)`.
#[must_use]
pub fn armed(meta: (FnId, FnId), cell: (FnId, FnId, FnId, FnId)) -> Self {
Owner

armed is public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.

`armed` is public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.
The meta-substrate half: the freestanding actor now emits the EXACT libc
event sequence (verified from ck_rt source, not memory): Spawned{0,root}
-> Spawned{1,parent 0} -> Despawned{Normal} -> ArenaHighWater ->
ArenaReclaimed (tags 0/0/1/8/9), crash runs inserting ActorCrashed(10)
before Despawned{Crashed} — decoded by the UNCHANGED host reader, gated
by a SHAPE-DIFF test against libc helloWorld (the plural-lowerings
contract as a real assertion) + field-by-field + crash-twin tests.

Meta.ck grows the per-event fns (spawned_root/spawned/despawned/
arena_high_water/arena_reclaimed/crashed_at; FS6 word-store framing);
Supervisor.ck's defer-driven teardown emits despawn -> high-water (read
off the region header: cursor - 16 = the honest measured peak of a
never-freeing bump arena) -> evict -> os_release -> reclaimed, on BOTH
exits (LC10 propagation runs it on crash). New special form
Supervisor::raise_word(offset) -> Load(RaiseSlot, offset) (const,
word-aligned, in-layout only) so the freestanding ActorCrashed carries
the REAL raise site span. The FS7a scope edge closes: the actor path
composes cross-module direct callees (Supervisor::run -> Meta::*).

fd design: per-emit openat O_APPEND (ordered stream; fd = documented
bounded leak, no close leaf yet). u8 Memory width deliberately NOT
shipped (dead sliver without a loop construct); kind/error/hint empty +
timestamps 0 with residue notes. Bare-fn-main freestanding crash stays
stderr-only (FS-test/FS8's, declared).

Gates: fmt clean, clippy 0 warnings, cargo test --workspace 1167 passed
/ 0 failed (108 suites); freestanding_actor_events_run 3/3 re-verified;
libc paths byte-identical.
Locked ahead of the multi-actor phase (plan.md row 14): cooperative
yields (no preemption; an actor runs until send/receive-wait/yield);
memory writing is sequential — ONE writer at a time in a queue, first
come first serve (the scheduler's run queue IS the writer queue); two
concurrent calls both yield and the first to schedule wins — arrival
order is delivery order, deterministically. Composes with no-shared-
mutable + send-is-serialize + record/replay (same arrival order -> same
schedule -> same meta stream). Implication recorded: effectively single-
threaded-cooperative initially; a parallel scheduler is a separate later
decision. Still open for that phase (surface, don't invent): mailbox-full
behavior under bounded Mailbox<Msg,N>.
Owner refinement, same session: the one-writer invariant is per-state,
not per-machine — an actor's mailbox IS its writer queue; the global
FIFO is only the initial single-threaded implementation. Joined state ->
exactly one actor -> sequential by construction (the counter is the
point, not the limitation); independent work -> scale out actor count +
round-robin delivery = cheap parallelism (file reads), no locks because
shared mutable state cannot exist. Author's obligation recorded: design
state ownership consciously. A later parallel scheduler needs no new
memory rules.
Owner, 2026-07-02, load-bearing but NOT scheduled (plan.md §6 +
scheduler-model implication): (1) the cooperative model IS an event loop
— many actors, one core; multi-core = one scheduler per core over
share-nothing actors, no new memory rules needed; (2) stated target: a
snake-class SERVER under 65536 bytes, running directly in L1 — the
freestanding actor is already 9592 B, post-FS8 the binary-size line
becomes a trackable ceiling (threats: stdlib growth, mono bloat;
counter: treeshake); (3) CUDA lowering — Floor-core is the backend-
agnostic tier, a PTX emitter is another thin consumer (why Cranelift
stays mechanical/replaceable); bounded deterministic memory + no shared
mutable state + share-nothing actors make the language GPU-shaped.
Test.ck: per-test supervised run composing Supervisor's public forms
(new Supervisor.ckh) — fresh Arena::install + adopt, TestStarted with
the REAL test name, dispatch via LC7, verdict off the per-test failed
flag, evict + wholesale release; NO lifecycle/arena events (exactly
ck_rt_run_test's stream); emitted entry sums flags = suite exit.
Assert.ck: soft_check emits AssertEvaluated with real predicate/hint
text, accumulates wire-encoded FailedAssert entries for TestFailed, and
RETURNS — soft semantics preserved (assert stays a non-fatal meta
observation). Named soft_check/outcome — never check/equal (module items
would shadow the ambient op).

The String->region byte copy now EXISTS: Meta::append_str/str_len as
pass-desugared special forms riding LC4's byte-copy loop (TestFacts,
lower/test_lower.rs) — the FS6/FS7b mechanism residue is RETIRED (only
lifecycle VALUES still ship empty, narrowed in plan.md §4). Failed-flag
= ck_test_cell (two-word writable cell; head + entries regions, 4096 B
honest bound, four Test:: cell forms). Mocks freestanding: codegen-tier
alloc_target retargets mock region/list/copy-grow allocations to the
CkLang Arena::alloc; memcpy/memcmp swap for CFG loops; libc paths None
= bit-identical.

Gates observed: soft_asserts freestanding 10136 B (suite exit 1, the
intentional failing direction, stream event-identical to libc incl. the
FailedAssert list); greeter 14944 B 'greet_uses_the_name ok' exit 0,
stream event-identical; mock_state/mutate/dispatch/two_tests +
greeter_two_tests all passing freestanding. New gate
freestanding_test_run.rs diffs libc-vs-freestanding streams. The
greeter-test ungated-gap row CLOSES. What stays ck_rt: the libc paths
wholesale, until FS8.

Gates: fmt clean, clippy 0 warnings, cargo test --workspace exit 0
(111 binaries); greeter test re-verified live post-integration.
Ratchets: ck_codegen 586->594, ck_lower 545->550 (documented).
THE GOAL COMMIT (plan.md §1). Deleted: the ck_rt crate (~2300 LoC),
libck_rt.a from build/deploy, tests/corpus/stdlib_old/ (23 files), the
entire codegen libc tier (entry.rs, emit_actor_object/ck_rt_run_actor,
emit_tests_object/ck_rt_run_test, ck_rt_assert_check, ck_rt_alloc,
ck_rt_write_stdout, the intrinsic_target ck_rt symbol map, libc
memcpy/memcmp, both crash upcalls), LinkMode + both libc link fns + the
in-memory Runtime path. Net -6388/+1240 over 139 files. cargo tree:
zero ck_rt. Live code references: zero.

What remains native: the compiler, and crt/ck_start.s (_start +
ck_syscall — nine instructions). Every entry path (fn main, actor,
ckc test) links -nostdlib -static against ck_start.o only. The runtime
floor is CkLang: Arena, Meta, Supervisor, Test, Assert, Panic, Log,
StdoutLog, Sys, Memory.

Host-side decoding relocated to new ck_wire crate (993 LoC, dep ck_span;
the write half kept as the executable wire SPEC pinned by round-trip
tests — no production Rust emits events; Meta.ck is the sole emitter).
The meta-stream contract survives deletion as CHECKED-IN GOLDENS
(pre-deletion libc streams captured as raw wire bytes; freestanding
streams diff against them — real assertions, not tautologies).

SESSION STOP observables (fs8_default_run.rs, DEFAULT path, verified
live post-integration): helloWorld -> 'hello, world', 10000 B, 5-event
stream field-asserted + golden-diffed, nm -u empty, no dynamic section;
greeter run 'Hi, world' 10312 B + test greet_uses_the_name ok. helloWorld
+ greeter trap.md retired (their declared condition met), both in
RUNTIME_TESTED. Corpus long tail reconciled vs the live stdlib (only
route/trap.md ratcheted — 4 E0301 column shifts); calc/counter/
fibonacci/snake/todo/route stay honestly diagnostics-only. --mem sizes
the armed path (counts Call(Arena::alloc) by stamped type) — greeter
now 16 B bounded + bare-String unbounded vs measured 9, strictly more
honest than pre-FS8.

Ledger: every plan.md §2 libc half RETIRED (dated); §4 residues each
carry a named future slice (empty lifecycle values, timestamps 0,
fd-unclosed, per-event fns, fn-main crash stderr-only, 4096-B entries
bound). stdlib.md FS8 row SESSION STOP + §9.7 close-out;
current-state.md pipeline diagram loses ck_rt.

Gates: fmt clean, clippy 0 warnings, cargo test --workspace 1134 passed
/ 0 failed, cargo tree ck_rt empty, default-path observables re-verified
live by the orchestrator.
dferreira manually merged commit a9ca4b20ff into master 2026-03-03 14:07:20 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/cklang-bootstrap!49
No description provided.