summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootvere.sh6
-rw-r--r--ocaml/BOOT_FLOW.md172
-rw-r--r--ocaml/BOOT_PROCESS.md47
-rw-r--r--ocaml/FINDINGS.md113
-rw-r--r--ocaml/lib/boot.ml271
-rw-r--r--ocaml/test/dune10
-rw-r--r--ocaml/test/examine_ivory.ml84
-rw-r--r--ocaml/test/test_life_formula.ml48
-rw-r--r--ocaml/test/test_two_stage_boot.ml27
-rw-r--r--vere/build.zig10
-rw-r--r--vere/pkg/noun/life_test.c96
-rw-r--r--vere/pkg/noun/vortex.c28
-rw-r--r--vere/pkg/vere/boot_tests.c48
-rw-r--r--vere/pkg/vere/king.c7
-rw-r--r--vere/pkg/vere/main.c2
-rw-r--r--vere/pkg/vere/mars.c34
-rw-r--r--vere/pkg/vere/solid_boot_test.c178
-rw-r--r--zod/.urb/log/0i0/data.mdbbin12832768 -> 12832768 bytes
-rw-r--r--zod/.urb/log/0i0/lock.mdbbin8192 -> 8192 bytes
-rw-r--r--zod/.urb/log/0i0/vere.txt2
-rw-r--r--zod/.vere.lock2
21 files changed, 1100 insertions, 85 deletions
diff --git a/bootvere.sh b/bootvere.sh
index 8c3fd8a..1186667 100644
--- a/bootvere.sh
+++ b/bootvere.sh
@@ -1 +1,5 @@
-vere/zig-out/x86_64-linux-musl/urbit -B ocaml/solid.pill -F zod -v
+cd vere
+zig build
+cd ..
+rm -rf ./zod
+vere/zig-out/x86_64-linux-musl/urbit -B ocaml/solid.pill -F zod
diff --git a/ocaml/BOOT_FLOW.md b/ocaml/BOOT_FLOW.md
new file mode 100644
index 0000000..7f9d417
--- /dev/null
+++ b/ocaml/BOOT_FLOW.md
@@ -0,0 +1,172 @@
+# Complete Boot Flow in C Vere (`-B solid.pill`)
+
+## The Simple Truth
+
+When you run `vere -B solid.pill`, here's exactly what happens:
+
+### Step 0: Command Line Processing
+
+**Location**: `main.c:418`
+```c
+case 'B':
+ u3_Host.ops_u.pil_c = _main_repath(optarg); // Store pill filepath
+```
+
+### Step 1: Load Pill File (`_boothack_pill`)
+
+**Location**: `king.c:611-613`
+```c
+if ( 0 != u3_Host.ops_u.pil_c ) {
+ u3l_log("boot: loading pill %s", u3_Host.ops_u.pil_c);
+ pil = u3m_file(u3_Host.ops_u.pil_c); // Load raw bytes into memory
+}
+```
+
+Returns: `[pil arv]` where `pil` is raw pill bytes, `arv` is optional filesystem
+
+### Step 2: Cue the Pill Bytes (`u3_mars_boot`)
+
+**Location**: `mars.c:1958`
+```c
+u3_weak jar = u3s_cue_xeno(len_d, hun_y); // Cue the pill bytes
+if ( (u3_none == jar) || (c3n == u3r_p(jar, c3__boot, &com)) ) {
+ fprintf(stderr, "boot: parse fail\r\n");
+ exit(1);
+}
+```
+
+Expects pill structure: `[%boot com]`
+
+### Step 3: Extract Events from Pill (`_mars_boot_make`)
+
+**Location**: `mars.c:1971`
+```c
+_mars_sift_pill(u3k(pil), &bot, &mod, &use, &cax)
+```
+
+Pill structure: `[%pill %solid [bot mod use]]`
+- `bot`: Lifecycle events (boot sequence)
+- `mod`: Module/vane events
+- `use`: Userspace/app events
+
+### Step 4: Write Events to Disk
+
+**Location**: `mars.c:1985-1987`
+```c
+u3_disk_plan_list(log_u, ova); // Write events to LMDB
+u3_disk_sync(log_u); // Sync to disk
+```
+
+### Step 5: Read Events Back and Boot
+
+**Location**: `mars.c:1993`
+```c
+_mars_do_boot(log_u, log_u->dun_d, cax); // Boot from disk
+```
+
+Inside `_mars_do_boot` (line 1107):
+```c
+eve = u3_disk_read_list(log_u, 1, eve_d, &mug_l); // Read from disk
+```
+
+Then (line 1160):
+```c
+u3v_boot(eve); // Boot with event list!
+```
+
+## Event Structure Details
+
+### Event Creation in `_mars_boot_make` (`mars.c:1814-1836`)
+```c
+u3_noun now = u3_time_in_tv(&inp_u->tim_u);
+u3_noun eve = u3kb_flop(bot); // Start with bot events (NO TIMESTAMP!)
+
+u3_noun lit = u3kb_weld(mod, use);
+while ( u3_nul != t ) {
+ u3x_cell(t, &i, &t);
+ now = u3ka_add(now, u3k(bit));
+ eve = u3nc(u3nc(u3k(now), u3k(i)), eve); // Add timestamped event [now i]
+}
+
+*ova = u3kb_flop(eve); // Final event list
+```
+
+**KEY**: Bot events are **NOT timestamped**, mod/use events ARE timestamped as `[timestamp event]`
+
+### Step 4: Write Events to Disk
+
+**Location**: `mars.c:1955`
+```c
+u3_disk_plan_list(log_u, ova); // Write events to LMDB
+u3_disk_sync(log_u); // Sync to disk
+```
+
+### Step 5: Read Events Back and Boot (`_mars_do_boot`)
+
+**Location**: `mars.c:1107`
+```c
+eve = u3_disk_read_list(log_u, 1, eve_d, &mug_l); // Read from disk
+```
+
+Each event is cued from disk:
+```c
+// disk.c:
+*job = u3ke_cue(u3i_bytes(len_i - 4, dat_y + 4));
+ven_u->eve = u3nc(job, ven_u->eve); // Cons onto list
+return u3kb_flop(ven_u->eve); // Return flipped list
+```
+
+## Summary: The Complete `-B` Flow
+
+1. **Load**: `u3m_file()` loads pill bytes from filepath
+2. **Cue**: `u3s_cue_xeno()` deserializes to `[%boot com]` structure
+3. **Parse**: `_mars_boot_make()` extracts bot/mod/use events and timestamps them
+4. **Persist**: `u3_disk_plan_list()` writes to LMDB, `u3_disk_sync()` commits
+5. **Read**: `u3_disk_read_list()` reads events back from disk
+6. **Boot**: `u3v_boot(eve)` boots with the event list
+
+## What We Need to Implement in OCaml
+
+Just follow the same pattern:
+
+```ocaml
+(* Load pill file *)
+let pill_bytes = load_file pill_path in
+
+(* Cue the pill *)
+let pill_noun = Serial.cue pill_bytes in
+
+(* Extract [%boot com] *)
+let com = extract_boot_structure pill_noun in
+
+(* Parse into bot/mod/use events *)
+let (bot, mod_, use_) = parse_pill_structure com in
+
+(* Timestamp mod/use events (bot events stay bare) *)
+let event_list = build_event_list bot mod_ use_ in
+
+(* For now, skip disk persistence and boot directly *)
+Boot.boot event_list
+```
+
+**Key Insight**: Bot events are **NOT timestamped** (they're bare nouns), while mod/use events **ARE timestamped** as `[timestamp event]` pairs.
+
+## The "eve=null" Mystery Solved
+
+From the actual boot log:
+```
+lite: arvo formula 4ce68411
+u3v_life: eve=null (atom=yes cell=no) ← First call (ivory pill)
+u3v_life: completed successfully
+lite: core 641296f
+
+_mars_do_boot: first event is atom ← Bot events are atoms!
+u3v_boot: processing 10 events
+u3v_life: eve=null (atom=yes cell=no) ← Second call (from vortex.c)
+```
+
+There are **TWO** `u3v_life` calls:
+1. First: Ivory pill bootstrap (eve=null creates initial kernel)
+2. Second: Inside `u3v_boot()` which processes the 10 events
+
+The `eve=null` log in the second call is likely referring to the **original** `eve` parameter before it's been processed, NOT the actual subject passed to the lifecycle formula!
diff --git a/ocaml/BOOT_PROCESS.md b/ocaml/BOOT_PROCESS.md
index 08987ef..5a621b6 100644
--- a/ocaml/BOOT_PROCESS.md
+++ b/ocaml/BOOT_PROCESS.md
@@ -57,6 +57,53 @@ CLAUDE:
**Status in OCaml:** ❌ Not applicable for solid pill boot path
+---
+
+## ✨ CRITICAL DISCOVERY: Ivory Pill Structure (2025-01-06)
+
+**Finding:** The ivory.pill file has structure `["ivory" ARVO_CORE]` where:
+- Tag: The atom/string "ivory"
+- Tail: **A CELL containing the complete Arvo core** (NOT atom 0!)
+
+**Verified by:** `test/examine_ivory.ml` which loads and analyzes ivory.pill:
+```
+Tag: ivory
+Tail type: CELL
+Tail is a CELL
+Head type: cell
+Tail type: cell
+✓ Has slot 2 and 3 (it's a cell with head and tail)
+```
+
+**This means:**
+1. The lifecycle formula `[2 [0 3] [0 2]]` operates on the **Arvo core** (a cell), not on atom 0
+2. The formula extracts slot 3 (tail of core) and slot 2 (head of core), then nocks them together
+3. This is why the formula doesn't fail - it's working on a valid cell structure!
+
+**Boot sequence clarification:**
+- `_cv_lite()` extracts `tal` from `["ivory" tal]` → `tal` is the **Arvo core**
+- `u3v_life(tal)` runs the lifecycle formula **on the Arvo core itself**
+- The result is the updated/initialized Arvo kernel
+
+**Mystery resolved:**
+The confusing C Vere log showing "eve=null" must refer to a DIFFERENT usage context (possibly for solid pills or after the ivory is bootstrapped). The ivory pill tail is definitely a CELL, not null!
+
+**NEW PROBLEM DISCOVERED:**
+When attempting to run the lifecycle formula on the ivory.pill tail:
+- Step 1 succeeds: `*[arvo_core [0 3]]` → extracts slot 3 (a cell)
+- Step 2 succeeds: `*[arvo_core [0 2]]` → extracts slot 2 (a cell)
+- Step 3 **FAILS**: `*[slot3 slot2]` → Nock Exit!
+
+This means the ivory.pill file's Arvo core **cannot be executed** by the lifecycle formula!
+
+**HYPOTHESIS:**
+The embedded ivory pill in C Vere (from ivory.c) might be DIFFERENT from the ivory.pill FILE. The embedded version might actually be `["ivory" 0]` or some other structure that works with the lifecycle formula.
+
+**NEXT STEP:**
+Need to check what C Vere actually does when you boot with `-B solid.pill`. Does it:
+1. Load embedded ivory first? OR
+2. Skip ivory entirely and boot directly from solid pill?
+
```c
diff --git a/ocaml/FINDINGS.md b/ocaml/FINDINGS.md
new file mode 100644
index 0000000..397924c
--- /dev/null
+++ b/ocaml/FINDINGS.md
@@ -0,0 +1,113 @@
+# Critical Boot System Findings (2025-01-06)
+
+## Summary
+We discovered that the ivory.pill file structure is `["ivory" ARVO_CORE]` where the tail is a CELL containing the complete Arvo core, NOT atom 0. However, the lifecycle formula `[2 [0 3] [0 2]]` FAILS when executed on this Arvo core.
+
+## Detailed Findings
+
+### 1. Ivory Pill Structure
+**Verified by:** `test/examine_ivory.ml`
+
+```
+Tag: ivory
+Tail type: CELL
+ Head type: cell
+ Tail type: cell
+✓ Has slot 2 and 3
+```
+
+The ivory.pill file contains:
+- Tag: The atom "ivory"
+- Tail: **A complete Arvo core (CELL)** - NOT null/atom 0!
+
+### 2. Lifecycle Formula Execution Test
+**Tested by:** `test/test_two_stage_boot.ml`
+
+Running `[2 [0 3] [0 2]]` on the Arvo core:
+- ✓ Step 1: `*[core [0 3]]` succeeds → returns cell
+- ✓ Step 2: `*[core [0 2]]` succeeds → returns cell
+- ✗ Step 3: `*[slot3 slot2]` **FAILS with Nock Exit**
+
+### 3. What This Means
+
+The ivory.pill file's Arvo core **cannot be bootstrapped** using the lifecycle formula in our implementation! This leads to two possibilities:
+
+**Possibility A:** The embedded ivory in C Vere is DIFFERENT from ivory.pill
+- Embedded might be `["ivory" 0]` or some simpler structure
+- ivory.pill file might be for a different use case
+
+**Possibility B:** C Vere's `-B solid.pill` path doesn't use ivory at all
+- Your log showed `u3v_life: eve=null` during solid pill boot
+- Maybe solid pill boot skips the ivory pill entirely?
+- The `u3v_boot_lite()` code in mars.c was COMMENTED OUT!
+
+## Questions to Answer
+
+1. **Does `-B solid.pill` load ivory.pill at all?**
+ - Need to trace mars.c boot path for `-B` flag
+ - Check if embedded ivory is even used
+
+2. **What is eve=null in your C Vere log?**
+ - If eve is null (atom 0), how does `[2 [0 3] [0 2]]` work on it?
+ - Our test proves it CANNOT work on atom 0!
+
+3. **Is ivory.pill the right file to use?**
+ - Maybe there's a different pill for bootstrapping?
+ - Or maybe solid.pill contains everything needed?
+
+## C Vere Test Results
+
+**Test Created**: `vere/pkg/noun/life_test.c`
+**Built with**: `zig build life-test`
+
+**Result**:
+```
+Testing lifecycle formula [2 [0 3] [0 2]] on null
+
+Formula: [2 [0 3] [0 2]]
+Subject: 0 (null)
+
+✗ FAILED!
+Error: : %exit
+
+This confirms the formula CANNOT work on atom 0!
+```
+
+**PROVEN**: The lifecycle formula `[2 [0 3] [0 2]]` **FAILS** on atom 0 in C Vere!
+
+## The Contradiction
+
+Your boot log showed:
+```
+u3v_boot: processing 10 events
+u3v_life: eve=null (atom=yes cell=no)
+u3v_life: completed successfully
+```
+
+But our C test PROVES this is impossible! The lifecycle formula cannot succeed on null!
+
+## Possible Explanations
+
+1. **"eve=null" doesn't mean the lifecycle formula is called on null**
+ - Maybe it's just logging what the original `eve` was before processing?
+ - The actual u3v_life call might receive something else?
+
+2. **The log is from a DIFFERENT u3v_life call**
+ - Maybe there are TWO separate calls to u3v_life?
+ - One with ivory pill's core (succeeds)
+ - One that's never actually executed (logged as null)?
+
+3. **There's special case handling we're missing**
+ - Maybe C Vere checks for null and skips u3v_life entirely?
+ - The log might be misleading?
+
+## Next Steps
+
+1. Add MORE detailed logging to C Vere's u3v_life to see:
+ - Exact eve structure being passed
+ - Whether formula actually executes
+ - What the result is
+
+2. Trace where u3v_life is called from during `-B solid.pill` boot
+
+3. Check if there's special handling for null in u3v_boot or mars.c
diff --git a/ocaml/lib/boot.ml b/ocaml/lib/boot.ml
index 630da3b..ce30f2f 100644
--- a/ocaml/lib/boot.ml
+++ b/ocaml/lib/boot.ml
@@ -148,12 +148,6 @@ let boot_fake state =
*)
let life eve =
try
- (* Build lifecycle formula: [2 [0 3] [0 2]] *)
- let lyf = Noun.cell (Noun.atom 2)
- (Noun.cell
- (Noun.cell (Noun.atom 0) (Noun.atom 3))
- (Noun.cell (Noun.atom 0) (Noun.atom 2))) in
-
Printf.printf "[Boot] Running lifecycle formula [2 [0 3] [0 2]]...\n%!";
(* Check if eve is null (for ivory pill boot) *)
@@ -177,9 +171,27 @@ let life eve =
end;
(* Run lifecycle formula *)
+ Printf.printf "[Boot] About to execute: *[eve [2 [0 3] [0 2]]]\n%!";
+ Printf.printf "[Boot] This expands to: *[*[eve [0 3]] *[eve [0 2]]]\n%!";
+
+ (* First, manually compute the two parts to see where it fails *)
let gat =
try
- Nock.nock_on eve lyf
+ (* Step 1: Compute *[eve [0 3]] = slot 3 of eve *)
+ Printf.printf "[Boot] Step 1: Computing *[eve [0 3]] (slot 3 of subject)...\n%!";
+ let slot3_result = Nock.nock_on eve (Noun.cell (Noun.atom 0) (Noun.atom 3)) in
+ Printf.printf "[Boot] ✓ Slot 3 computed: %s\n%!"
+ (if Noun.is_cell slot3_result then "cell" else "atom");
+
+ (* Step 2: Compute *[eve [0 2]] = slot 2 of eve *)
+ Printf.printf "[Boot] Step 2: Computing *[eve [0 2]] (slot 2 of subject)...\n%!";
+ let slot2_result = Nock.nock_on eve (Noun.cell (Noun.atom 0) (Noun.atom 2)) in
+ Printf.printf "[Boot] ✓ Slot 2 computed: %s\n%!"
+ (if Noun.is_cell slot2_result then "cell" else "atom");
+
+ (* Step 3: Compute *[slot3_result slot2_result] *)
+ Printf.printf "[Boot] Step 3: Computing *[slot3 slot2] (nock slot-2 formula on slot-3 subject)...\n%!";
+ Nock.nock_on slot3_result slot2_result
with e ->
Printf.printf "[Boot] ✗ Nock failed during lifecycle: %s\n%!"
(Printexc.to_string e);
@@ -237,56 +249,205 @@ let boot state eve_list =
with e ->
Error ("Boot failed: " ^ Printexc.to_string e)
-(* Boot from ivory pill - the lightweight boot sequence
+(* Parse solid pill structure: [%boot [%pill %solid [bot mod use]]]
+ *
+ * Following C Vere mars.c:1730 _mars_sift_pill
+ *)
+let parse_solid_pill pil =
+ (* Extract [%boot com] *)
+ if not (Noun.is_cell pil) then
+ Error "Pill must be a cell"
+ else
+ let tag = Noun.head pil in
+ let com = Noun.tail pil in
+
+ (* Check for %boot tag *)
+ let boot_tag = Z.of_string "1953654151028" in (* "boot" *)
+
+ match tag with
+ | Noun.Atom z when Z.equal z boot_tag ->
+ (* Now parse com structure *)
+ if not (Noun.is_cell com) then
+ Error "Pill com must be a cell"
+ else
+ (* com is [[pill typ] [bot mod use]] *)
+ let fst = Noun.head com in
+ let snd = Noun.tail com in
+
+ if not (Noun.is_cell fst) then
+ Error "Pill fst must be a cell"
+ else
+ let pill_tag = Noun.head fst in
+ let _typ = Noun.tail fst in
+
+ (* Check for %pill tag *)
+ let pill_atom = Z.of_string "1819633778" in (* "pill" *)
+
+ match pill_tag with
+ | Noun.Atom z when Z.equal z pill_atom ->
+ (* Extract [bot mod use] from snd *)
+ if not (Noun.is_cell snd) then
+ Error "Events structure must be a cell"
+ else
+ let bot = Noun.head snd in
+ let rest = Noun.tail snd in
+
+ if not (Noun.is_cell rest) then
+ Error "Mod/use structure must be a cell"
+ else
+ let mod_ = Noun.head rest in
+ let use = Noun.tail (Noun.head (Noun.tail rest)) in
+
+ Ok (bot, mod_, use)
+ | _ ->
+ Error "Expected %pill tag"
+ | _ ->
+ Error "Expected %boot tag"
+
+(* Build event list following C Vere mars.c:1814-1836
*
- * Ivory pills have structure: ["ivory" core]
- * The core contains a lifecycle formula that must be executed
+ * Key: Bot events are NOT timestamped (bare atoms/cells)
+ * Mod/use events ARE timestamped as [timestamp event]
*)
-let boot_ivory ~fs state pill_path =
- Printf.printf "[Boot] Booting from ivory pill...\n%!";
+let build_event_list bot mod_ use_ =
+ (* Count events *)
+ let rec count_list noun =
+ match noun with
+ | Noun.Atom z when Z.equal z Z.zero -> 0
+ | Noun.Cell (_, rest) -> 1 + count_list rest
+ | _ -> 0
+ in
- match load_pill ~fs pill_path with
- | Error err ->
- let msg = match err with
- | FileNotFound s -> "File not found: " ^ s
- | InvalidPill s -> "Invalid pill: " ^ s
- | BootFailed s -> "Boot failed: " ^ s
- in
- Printf.printf "[Boot] Error: %s\n%!" msg;
+ let bot_c = count_list bot in
+ let mod_c = count_list mod_ in
+ let use_c = count_list use_ in
+
+ Printf.printf "[Boot] Building event list:\n%!";
+ Printf.printf " Bot events: %d (NO timestamp)\n%!" bot_c;
+ Printf.printf " Mod events: %d (WITH timestamp)\n%!" mod_c;
+ Printf.printf " Use events: %d (WITH timestamp)\n%!" use_c;
+ Printf.printf " Total: %d events\n%!" (bot_c + mod_c + use_c);
+
+ (* Get current time in Urbit format (128-bit @da timestamp) *)
+ let now_timeval = Unix.gettimeofday () in
+ (* Convert to microseconds since Unix epoch *)
+ let now_us = Int64.of_float (now_timeval *. 1_000_000.0) in
+ (* Urbit epoch is ~292 billion years before Unix epoch
+ For now, use simplified timestamp *)
+ let now = Noun.Atom (Z.of_int64 now_us) in
+
+ (* 1/2^16 seconds increment *)
+ let bit = Noun.Atom (Z.shift_left Z.one 48) in
+
+ (* Helper: flip a list *)
+ let rec flip acc noun =
+ match noun with
+ | Noun.Atom z when Z.equal z Z.zero -> acc
+ | Noun.Cell (h, t) -> flip (Noun.Cell (h, acc)) t
+ | _ -> acc
+ in
+
+ (* Start with flipped bot events (NO timestamp) *)
+ let eve = flip (Noun.Atom Z.zero) bot in
+
+ (* Weld mod and use lists *)
+ let rec weld l1 l2 =
+ match l1 with
+ | Noun.Atom z when Z.equal z Z.zero -> l2
+ | Noun.Cell (h, t) -> Noun.Cell (h, weld t l2)
+ | _ -> l2
+ in
+
+ let lit = weld mod_ use_ in
+
+ (* Add timestamped events *)
+ let rec add_timestamped acc now_ref noun =
+ match noun with
+ | Noun.Atom z when Z.equal z Z.zero -> acc
+ | Noun.Cell (event, rest) ->
+ (* Increment timestamp *)
+ let new_now = match !now_ref, bit with
+ | Noun.Atom n, Noun.Atom b -> Noun.Atom (Z.add n b)
+ | _ -> !now_ref
+ in
+ now_ref := new_now;
+
+ (* Create [timestamp event] pair *)
+ let stamped = Noun.Cell (new_now, event) in
+
+ (* Cons onto accumulator *)
+ let new_acc = Noun.Cell (stamped, acc) in
+
+ add_timestamped new_acc now_ref rest
+ | _ -> acc
+ in
+
+ let now_ref = ref now in
+ let eve_with_stamped = add_timestamped eve now_ref lit in
+
+ (* Flip final list *)
+ let ova = flip (Noun.Atom Z.zero) eve_with_stamped in
+
+ Printf.printf "[Boot] ✓ Event list built: %d events\n%!" (count_list ova);
+ ova
+
+(* Boot from solid pill - following C Vere -B flag logic
+ *
+ * This follows the exact flow from BOOT_FLOW.md:
+ * 1. Load pill bytes from file
+ * 2. Cue to get [%boot com] structure
+ * 3. Parse into bot/mod/use events
+ * 4. Timestamp mod/use events (bot stays bare)
+ * 5. Boot with event list
+ *
+ * Skipping disk persistence for now (steps 4-5 in C flow)
+ *)
+let boot_solid ~fs state pill_path =
+ Printf.printf "\n%!";
+ Printf.printf "═══════════════════════════════════════════════════\n%!";
+ Printf.printf " Solid Pill Boot (Following C Vere -B Logic)\n%!";
+ Printf.printf "═══════════════════════════════════════════════════\n\n%!";
+
+ (* Step 1: Load pill file *)
+ Printf.printf "[1] Loading %s...\n%!" pill_path;
+
+ let file_path = Eio.Path.(fs / pill_path) in
+ let pill_bytes = Eio.Path.load file_path |> Bytes.of_string in
+
+ Printf.printf " ✓ Loaded %d bytes\n\n%!" (Bytes.length pill_bytes);
+
+ (* Step 2: Cue the pill *)
+ Printf.printf "[2] Cuing pill...\n%!";
+ let pil = Serial.cue pill_bytes in
+ Printf.printf " ✓ Cued successfully\n\n%!";
+
+ (* Step 3: Parse pill structure *)
+ Printf.printf "[3] Parsing pill structure...\n%!";
+ match parse_solid_pill pil with
+ | Error msg ->
+ Printf.printf " ✗ Parse failed: %s\n%!" msg;
Error msg
- | Ok pill ->
- (* Check if pill has ivory tag *)
- if not (Noun.is_cell pill.kernel) then
- Error "Ivory pill must be a cell"
- else begin
- let hed = Noun.head pill.kernel in
- let tal = Noun.tail pill.kernel in
-
- (* Check for "ivory" tag *)
- (* "ivory" as cord (little-endian): 0x79726f7669 = 521610950249 *)
- let ivory_tag = Z.of_string "521610950249" in
-
- match hed with
- | Noun.Atom z when Z.equal z ivory_tag ->
- Printf.printf "[Boot] ✓ Found ivory tag\n%!";
- Printf.printf "[Boot] Running lifecycle formula...\n%!";
-
- (try
- let start = Unix.gettimeofday () in
- let core = life tal in
- let elapsed = Unix.gettimeofday () -. start in
-
- Printf.printf "[Boot] ✓ Lifecycle completed in %.4fs\n%!" elapsed;
- Printf.printf "[Boot] Setting Arvo core...\n%!";
-
- State.boot state core;
- Printf.printf "[Boot] ✓ Ivory pill booted!\n%!";
- Ok ()
- with e ->
- Error ("Lifecycle failed: " ^ Printexc.to_string e))
-
- | _ ->
- Printf.printf "[Boot] Warning: No ivory tag found, trying regular boot...\n%!";
- boot_from_pill state pill
- end
+ | Ok (bot, mod_, use_) ->
+ Printf.printf " ✓ Extracted bot/mod/use events\n\n%!";
+
+ (* Step 4: Build event list (C Vere style) *)
+ Printf.printf "[4] Building event list (C Vere style)...\n%!";
+ let ova = build_event_list bot mod_ use_ in
+ Printf.printf "\n%!";
+
+ (* Step 5: Boot with event list *)
+ Printf.printf "[5] Calling u3v_boot with event list...\n%!";
+ Printf.printf " (Booting Arvo kernel from events)\n\n%!";
+
+ match boot state ova with
+ | Error msg ->
+ Printf.printf " ✗ BOOT FAILED: %s\n%!" msg;
+ Error msg
+ | Ok () ->
+ Printf.printf " ✓ BOOT SUCCEEDED!\n%!";
+ Printf.printf "\n%!";
+ Printf.printf "═══════════════════════════════════════════════════\n%!";
+ Printf.printf " ✓ SOLID PILL BOOT COMPLETE!\n%!";
+ Printf.printf "═══════════════════════════════════════════════════\n\n%!";
+ Ok ()
diff --git a/ocaml/test/dune b/ocaml/test/dune
index 17e84b8..c7cf6da 100644
--- a/ocaml/test/dune
+++ b/ocaml/test/dune
@@ -271,3 +271,13 @@
(name test_two_stage_boot)
(modules test_two_stage_boot)
(libraries nock_lib eio_main unix))
+
+(executable
+ (name test_life_formula)
+ (modules test_life_formula)
+ (libraries nock_lib))
+
+(executable
+ (name examine_ivory)
+ (modules examine_ivory)
+ (libraries nock_lib eio_main))
diff --git a/ocaml/test/examine_ivory.ml b/ocaml/test/examine_ivory.ml
new file mode 100644
index 0000000..34b5fed
--- /dev/null
+++ b/ocaml/test/examine_ivory.ml
@@ -0,0 +1,84 @@
+(* Examine ivory.pill structure *)
+
+open Nock_lib
+
+let main env =
+ Printf.printf "\n═══════════════════════════════════════\n";
+ Printf.printf " Examining ivory.pill Structure\n";
+ Printf.printf "═══════════════════════════════════════\n\n";
+
+ (* Load ivory pill *)
+ Printf.printf "[1] Loading ivory.pill...\\n%!";
+ let fs = Eio.Stdenv.fs env in
+ let pill_bytes = Eio.Path.(load (fs / "ivory.pill")) |> Bytes.of_string in
+ Printf.printf " Size: %d bytes\n%!" (Bytes.length pill_bytes);
+
+ (* Cue it *)
+ Printf.printf "[2] Cuing ivory pill...\n%!";
+ let pill = Serial.cue pill_bytes in
+ Printf.printf " ✓ Cued\n\n";
+
+ (* Check structure *)
+ Printf.printf "[3] Structure analysis:\n";
+ match pill with
+ | Noun.Cell (tag, tail) ->
+ (* Print tag *)
+ let tag_str = match tag with
+ | Noun.Atom z ->
+ let bytes = Z.to_bits z in
+ if String.length bytes <= 10 then bytes else Printf.sprintf "<atom %d bits>" (Z.numbits z)
+ | Noun.Cell _ -> "<cell>"
+ in
+ Printf.printf " Tag: %s\n" tag_str;
+
+ (* Analyze tail *)
+ Printf.printf " Tail type: %s\n" (if Noun.is_cell tail then "CELL" else "ATOM");
+
+ (match tail with
+ | Noun.Atom z when Z.equal z Z.zero ->
+ Printf.printf " Tail value: 0 (NULL!)\n";
+ Printf.printf "\n";
+ Printf.printf " ✓ CONFIRMED: Embedded ivory has structure [\"ivory\" 0]\n";
+ Printf.printf " This means u3v_life() is called with atom 0!\n"
+
+ | Noun.Atom z ->
+ Printf.printf " Tail value: atom with %d bits\n" (Z.numbits z);
+ Printf.printf " Tail decimal: %s\n" (Z.to_string z)
+
+ | Noun.Cell (h, t) ->
+ Printf.printf " Tail is a CELL\n";
+ Printf.printf " Head type: %s\n" (if Noun.is_cell h then "cell" else "atom");
+ Printf.printf " Tail type: %s\n" (if Noun.is_cell t then "cell" else "atom");
+
+ (* Check if it's the Arvo core structure *)
+ Printf.printf "\n Checking if tail is Arvo core...\n";
+ begin try
+ let _slot2 = Noun.slot (Z.of_int 2) tail in
+ let _slot3 = Noun.slot (Z.of_int 3) tail in
+ Printf.printf " ✓ Has slot 2 and 3 (it's a cell with head and tail)\n";
+
+ (* Check for lifecycle formula at slot 2 *)
+ begin try
+ let slot2 = Noun.slot (Z.of_int 2) tail in
+ Printf.printf " Slot 2 type: %s\n" (if Noun.is_cell slot2 then "cell" else "atom");
+
+ (* The lifecycle formula should be [2 [0 3] [0 2]] *)
+ match slot2 with
+ | Noun.Cell (Noun.Atom op, _rest) when Z.equal op (Z.of_int 2) ->
+ Printf.printf " Slot 2 starts with opcode 2 - could be lifecycle formula!\n";
+ Printf.printf "\n ✓ Tail appears to BE the Arvo core itself!\n";
+ Printf.printf " This means the lifecycle formula operates on the CORE, not null!\n"
+ | _ ->
+ Printf.printf " Slot 2 doesn't match expected lifecycle formula pattern\n"
+ with _ ->
+ Printf.printf " Could not analyze slot 2\n"
+ end
+ with Noun.Exit ->
+ Printf.printf " ✗ Cannot access slots 2/3 - not a valid cell structure\n"
+ end
+ )
+
+ | Noun.Atom _ ->
+ Printf.printf " ✗ Pill is an atom (unexpected)\n"
+
+let () = Eio_main.run main
diff --git a/ocaml/test/test_life_formula.ml b/ocaml/test/test_life_formula.ml
new file mode 100644
index 0000000..722154b
--- /dev/null
+++ b/ocaml/test/test_life_formula.ml
@@ -0,0 +1,48 @@
+(* Test lifecycle formula on atom 0 *)
+
+open Nock_lib
+
+let () =
+ Printf.printf "Testing lifecycle formula [2 [0 3] [0 2]] on atom 0\n\n%!";
+
+ (* Build the lifecycle formula *)
+ let lyf = Noun.cell (Noun.atom 2)
+ (Noun.cell
+ (Noun.cell (Noun.atom 0) (Noun.atom 3))
+ (Noun.cell (Noun.atom 0) (Noun.atom 2))) in
+
+ Printf.printf "Formula: [2 [0 3] [0 2]]\n%!";
+ Printf.printf "Subject: 0 (null)\n\n%!";
+
+ (* Try running it *)
+ begin try
+ let result = Nock.nock_on (Noun.atom 0) lyf in
+ Printf.printf "✓ SUCCESS! Result: %s\n%!" (match result with
+ | Noun.Atom z -> Z.to_string z
+ | Noun.Cell _ -> "[cell]")
+ with
+ | Noun.Exit ->
+ Printf.printf "✗ FAILED with Nock Exit\n%!";
+
+ (* Let's trace through the formula step by step *)
+ Printf.printf "\nStep-by-step trace:\n%!";
+ Printf.printf "Formula: *[0 [2 [0 3] [0 2]]]\n%!";
+ Printf.printf "Opcode 2: *[a [2 b c]] = *[*[a b] *[a c]]\n%!";
+ Printf.printf " b = [0 3]\n%!";
+ Printf.printf " c = [0 2]\n%!";
+ Printf.printf "\n*[a b] = *[0 [0 3]] = slot 3 of atom 0\n%!";
+
+ (* Try slot 3 on atom 0 *)
+ begin try
+ let s3 = Noun.slot (Z.of_int 3) (Noun.atom 0) in
+ Printf.printf " slot 3 of 0 = %s (unexpected!)\n%!" (match s3 with
+ | Noun.Atom z -> Z.to_string z
+ | Noun.Cell _ -> "[cell]")
+ with Noun.Exit ->
+ Printf.printf " slot 3 of 0 = ERROR (as expected)\n%!"
+ end;
+
+ Printf.printf "\nThis proves the formula CANNOT work on atom 0!\n%!"
+ | e ->
+ Printf.printf "✗ FAILED with: %s\n%!" (Printexc.to_string e)
+ end
diff --git a/ocaml/test/test_two_stage_boot.ml b/ocaml/test/test_two_stage_boot.ml
index 090dd50..f8311b5 100644
--- a/ocaml/test/test_two_stage_boot.ml
+++ b/ocaml/test/test_two_stage_boot.ml
@@ -40,16 +40,18 @@ let stage1_ivory_boot env =
Printf.printf " Tag: '%s'\n" tag_str;
Printf.printf " Core: %s\n\n" (if Noun.is_cell core then "cell" else "atom");
- (* Now boot with the ivory core as eve *)
- Printf.printf "[4] Running u3v_life() on ivory core...\n%!";
- Printf.printf " Formula: [2 [0 3] [0 2]]\n";
- Printf.printf " Subject: ivory pill core\n\n%!";
+ (* KEY DISCOVERY: The ivory pill tail IS the Arvo core! *)
+ Printf.printf "[4] Using ivory pill tail (Arvo core) for bootstrap...\n%!";
+ Printf.printf " Ivory structure: [\"ivory\" ARVO_CORE]\n";
+ Printf.printf " The tail is a CELL, not null!\n\n";
- let eve_core = core in (* Use the ivory core, not null! *)
+ Printf.printf "[5] Running u3v_life() on Arvo core...\n%!";
+ Printf.printf " Formula: [2 [0 3] [0 2]]\n";
+ Printf.printf " Subject: Arvo core (cell)\n%!";
begin try
let start = Unix.gettimeofday () in
- let kernel = Boot.life eve_core in
+ let kernel = Boot.life core in
let elapsed = Unix.gettimeofday () -. start in
Printf.printf " ✓ SUCCESS! Kernel built in %.4fs\n\n" elapsed;
@@ -186,18 +188,7 @@ let stage2_solid_boot env _ivory_kernel =
with
| Noun.Exit ->
Printf.printf " ✗ FAILED: Nock Exit during lifecycle\n\n";
-
- (* Debug: try with null like C Vere seems to do *)
- Printf.printf "[DEBUG] Trying with null (like C Vere)...\n%!";
- begin try
- let _kernel = Boot.life (Noun.atom 0) in
- Printf.printf " ✓ Null works! (same as ivory)\n";
- Printf.printf " This means solid pill events might be processed differently\n\n";
- false
- with _ ->
- Printf.printf " ✗ Null also fails\n\n";
- false
- end
+ false
| e ->
Printf.printf " ✗ FAILED: %s\n\n" (Printexc.to_string e);
diff --git a/vere/build.zig b/vere/build.zig
index 66318a0..707ca2b 100644
--- a/vere/build.zig
+++ b/vere/build.zig
@@ -616,6 +616,11 @@ fn buildBinary(
.file = "pkg/noun/serial_tests.c",
.deps = noun_test_deps,
},
+ .{
+ .name = "life-test",
+ .file = "pkg/noun/life_test.c",
+ .deps = noun_test_deps,
+ },
// pkg_vere
.{
.name = "ames-test",
@@ -628,6 +633,11 @@ fn buildBinary(
.deps = vere_test_deps,
},
.{
+ .name = "solid-boot-test",
+ .file = "pkg/vere/solid_boot_test.c",
+ .deps = vere_test_deps,
+ },
+ .{
.name = "newt-test",
.file = "pkg/vere/newt_tests.c",
.deps = vere_test_deps,
diff --git a/vere/pkg/noun/life_test.c b/vere/pkg/noun/life_test.c
new file mode 100644
index 0000000..cffe97b
--- /dev/null
+++ b/vere/pkg/noun/life_test.c
@@ -0,0 +1,96 @@
+/// @file
+/// Test lifecycle formula on different subjects
+
+#include "noun.h"
+
+/* _setup(): prepare for tests.
+*/
+static void
+_setup(void)
+{
+ u3m_boot_lite(1 << 28); // 256MB loom
+}
+
+/* Helper to run nock with subject+formula in a cell for u3m_soft */
+static u3_noun
+_nock_pair(u3_noun sub_fol)
+{
+ u3_noun sub, fol;
+ u3x_cell(sub_fol, &sub, &fol);
+ return u3n_nock_on(u3k(sub), u3k(fol));
+}
+
+/* _test_life_on_null(): test lifecycle formula on atom 0
+*/
+static c3_i
+_test_life_on_null(void)
+{
+ fprintf(stderr, "\n=================================================\n");
+ fprintf(stderr, "Testing lifecycle formula [2 [0 3] [0 2]] on null\n");
+ fprintf(stderr, "=================================================\n\n");
+
+ /* Build lifecycle formula: [2 [0 3] [0 2]] */
+ u3_noun lyf = u3nt(2, u3nc(0, 3), u3nc(0, 2));
+
+ fprintf(stderr, "Formula: [2 [0 3] [0 2]]\n");
+ fprintf(stderr, "Subject: 0 (null)\n");
+ fprintf(stderr, "Formula mug: %x\n\n", u3r_mug(lyf));
+
+ /* Test on null (atom 0) */
+ u3_noun eve = 0; /* u3_nul is defined as 0 */
+
+ fprintf(stderr, "Testing: *[0 [2 [0 3] [0 2]]]\n\n");
+
+ /* Try running it - need to pass [subject formula] as single argument */
+ u3_noun pair = u3nc(eve, lyf);
+ u3_noun pro = u3m_soft(0, _nock_pair, pair);
+
+ if ( u3_blip != u3h(pro) ) {
+ fprintf(stderr, "✗ FAILED!\n");
+ fprintf(stderr, "Error: ");
+ u3m_p("", u3h(pro));
+ fprintf(stderr, "\n");
+
+ /* Print trace if available */
+ if ( u3du(pro) ) {
+ fprintf(stderr, "\nTrace:\n");
+ u3m_p("trace", u3t(pro));
+ fprintf(stderr, "\n");
+ }
+
+ fprintf(stderr, "\nThis confirms the formula CANNOT work on atom 0!\n");
+ u3z(pro);
+ return 0;
+ }
+
+ fprintf(stderr, "✓ SUCCESS!\n");
+ fprintf(stderr, "Result mug: %x\n", u3r_mug(u3t(pro)));
+ fprintf(stderr, "\nResult structure:\n");
+ u3m_p("result", u3t(pro));
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "\nThis is UNEXPECTED! The formula somehow works on atom 0 in C!\n");
+
+ u3z(pro);
+
+ return 1;
+}
+
+int
+main(int argc, char* argv[])
+{
+ _setup();
+
+ if ( !_test_life_on_null() ) {
+ fprintf(stderr, "test life on null: failed (as expected)\r\n");
+ fprintf(stderr, "This is actually the CORRECT behavior - lifecycle formula cannot work on null.\r\n");
+ return 0; // Return success since failure is expected
+ }
+
+ // GC
+ //
+ u3m_grab(u3_none);
+
+ fprintf(stderr, "test life on null: unexpected success!\r\n");
+ return 0;
+}
diff --git a/vere/pkg/noun/vortex.c b/vere/pkg/noun/vortex.c
index 0d6d1cf..63b4fc2 100644
--- a/vere/pkg/noun/vortex.c
+++ b/vere/pkg/noun/vortex.c
@@ -25,14 +25,17 @@ u3v_home* u3v_Home;
u3_noun
u3v_life(u3_noun eve)
{
- c3_o is_null = u3r_sing(u3_nul, eve);
- c3_o is_atom = u3a_is_atom(eve);
- c3_o is_cell = u3du(eve);
+ u3l_log("u3f_life()");
+
- u3l_log("u3v_life: eve=%s (atom=%s cell=%s)",
- is_null ? "null" : (is_cell ? "cell" : "atom"),
- is_atom ? "yes" : "no",
- is_cell ? "yes" : "no");
+ c3_o is_atom = u3a_is_atom(eve);
+ c3_d len_d;
+ {
+ u3_noun len = u3qb_lent(eve);
+ u3_assert( c3y == u3r_safe_chub(len, &len_d) );
+ u3z(len);
+ }
+ u3l_log("u3v_life: processing %llu events", (unsigned long long)len_d);
u3_noun lyf = u3nt(2, u3nc(0, 3), u3nc(0, 2));
u3_noun gat = u3n_nock_on(eve, lyf);
@@ -48,6 +51,7 @@ u3v_life(u3_noun eve)
c3_o
u3v_boot(u3_noun eve)
{
+
c3_d len_d;
{
u3_noun len = u3qb_lent(eve);
@@ -105,8 +109,18 @@ _cv_lite(u3_noun pil)
c3_o
u3v_boot_lite(u3_noun pil)
{
+
// ensure zero-initialized kernel
//
+ c3_d len_d;
+ {
+ u3_noun len = u3qb_lent(pil);
+ u3_assert( c3y == u3r_safe_chub(len, &len_d) );
+ u3z(len);
+ }
+
+ u3l_log("u3v_boot_lite: processing %llu events", (unsigned long long)len_d);
+
u3A->roc = 0;
{
diff --git a/vere/pkg/vere/boot_tests.c b/vere/pkg/vere/boot_tests.c
index d9728d8..c90c538 100644
--- a/vere/pkg/vere/boot_tests.c
+++ b/vere/pkg/vere/boot_tests.c
@@ -17,11 +17,59 @@ _setup(void)
u3C.wag_w |= u3o_hashless;
u3m_boot_lite(1 << 26);
+ // this follows king.c
sil_u = u3s_cue_xeno_init_with(ur_fib27, ur_fib28);
if ( u3_none == (pil = u3s_cue_xeno_with(sil_u, len_d, byt_y)) ) {
printf("*** fail _setup 1\n");
exit(1);
}
+
+ u3l_log("embed_pil_is_atom %u", u3a_is_atom(pil));
+ u3_noun local_pil = u3m_file("/home/y/code/urbit/vere/ocaml/ivory.pill");
+ u3_noun cued_pil = u3ke_cue(local_pil);
+
+ u3l_log("local_pil_is_atom %u", u3a_is_atom(local_pil));
+ u3l_log("cued_pil_is_atom %u", u3a_is_atom(cued_pil));
+
+ c3_d len_pp;
+ {
+ u3_noun len = u3qb_lent(pil);
+ u3_assert( c3y == u3r_safe_chub(len, &len_pp) );
+ u3z(len);
+ }
+ u3l_log("embedded pill length %llu", (unsigned long long)len_pp);
+ c3_d len_pp2;
+ {
+ u3_noun len = u3qb_lent(cued_pil);
+ u3_assert( c3y == u3r_safe_chub(len, &len_pp2) );
+ u3z(len);
+ }
+ u3l_log("file_pill_length %llu", (unsigned long long)len_pp2);
+
+ // 1. Direct equality check
+ c3_o match = u3r_sing(pil, cued_pil);
+ u3l_log("pills equal: %u", match);
+
+ // 2. Compare mugs (32-bit hashes)
+ u3l_log("embedded mug: %x", u3r_mug(pil));
+ u3l_log("file mug: %x", u3r_mug(cued_pil));
+
+ // 3. If they're cells, compare heads
+ if ( c3y == u3a_is_cell(pil) && c3y == u3a_is_cell(cued_pil) ) {
+ u3l_log("head mug embedded: %x", u3r_mug(u3h(pil)));
+ u3l_log("head mug file: %x", u3r_mug(u3h(cued_pil)));
+ }
+
+ // 4. Jam both and compare sizes
+ u3_noun jam1 = u3qe_jam(pil);
+ u3_noun jam2 = u3qe_jam(cued_pil);
+ u3l_log("jammed embedded: %u met", u3r_met(3, jam1));
+ u3l_log("jammed file: %u met", u3r_met(3, jam2));
+ u3l_log("jams equal: %u", u3r_sing(jam1, jam2));
+ u3z(jam1); u3z(jam2);
+
+
+ //
u3s_cue_xeno_done(sil_u);
if ( c3n == u3v_boot_lite(pil) ) {
printf("*** fail _setup 2\n");
diff --git a/vere/pkg/vere/king.c b/vere/pkg/vere/king.c
index 0a2aef7..dd4711a 100644
--- a/vere/pkg/vere/king.c
+++ b/vere/pkg/vere/king.c
@@ -616,7 +616,7 @@ _boothack_pill(void)
c3_c url_c[2048];
if ( (c3y == u3_Host.ops_u.git) &&
- (0 != u3_Host.ops_u.arv_c) )
+ (0 != u3_Host.ops_u.arv_c) )
{
_git_pill_url(url_c, u3_Host.ops_u.arv_c);
}
@@ -714,9 +714,11 @@ static u3_noun
_boothack_doom(void)
{
u3_noun pax = u3i_string(u3_Host.dir_c);
+ u3m_p("pax boothack_doom", pax);
u3_noun bot;
if ( c3n == u3_Host.ops_u.nuu ) {
+ u3l_log("u3_Host.ops.u.nuu");
return u3nt(c3__pier, u3_nul, pax);
}
else if ( 0 != u3_Host.ops_u.fak_c ) {
@@ -961,6 +963,7 @@ _king_loop_exit()
static void
_king_boot_ivory(void)
{
+ u3l_log("king boot ivory");
c3_d len_d;
c3_y* byt_y;
@@ -1007,6 +1010,8 @@ _king_boot_ivory(void)
void
u3_king_commence()
{
+ u3l_log("king commence");
+
u3_Host.lup_u = uv_default_loop();
// initialize top-level timer
diff --git a/vere/pkg/vere/main.c b/vere/pkg/vere/main.c
index a547e23..db19f77 100644
--- a/vere/pkg/vere/main.c
+++ b/vere/pkg/vere/main.c
@@ -1293,6 +1293,8 @@ _cw_eval(c3_i argc, c3_c* argv[])
u3_cue_xeno* sil_u;
u3_weak pil;
+ u3l_log("initialize loom and load ivory main.c");
+
u3C.wag_w |= u3o_hashless;
u3m_boot_lite((size_t)1 << u3_Host.ops_u.lom_y);
sil_u = u3s_cue_xeno_init_with(ur_fib27, ur_fib28);
diff --git a/vere/pkg/vere/mars.c b/vere/pkg/vere/mars.c
index 5f9eb68..7b1ed03 100644
--- a/vere/pkg/vere/mars.c
+++ b/vere/pkg/vere/mars.c
@@ -1153,10 +1153,28 @@ _mars_do_boot(u3_disk* log_u, c3_d eve_d, u3_noun cax)
u3l_log("--------------- bootstrap starting ----------------");
- u3l_log("boot: 1-%u", u3qb_lent(eve));
+ c3_d eve_count = u3qb_lent(eve);
+ u3l_log("boot: 1-%llu", (unsigned long long)eve_count);
+ u3l_log("_mars_do_boot: eve list has %llu events", (unsigned long long)eve_count);
+
+ // Check structure of first event
+ if ( u3_nul != eve ) {
+ u3_noun first = u3h(eve);
+ c3_o is_atom = u3a_is_atom(first);
+ c3_o is_cell = u3a_is_cell(first);
+ u3l_log("_mars_do_boot: first event is %s", is_cell ? "cell" : (is_atom ? "atom" : "unknown"));
+
+ if ( is_cell ) {
+ u3_noun fst_h = u3h(first);
+ u3_noun fst_t = u3t(first);
+ u3l_log(" first event head: %s", u3a_is_atom(fst_h) ? "atom (timestamp?)" : "cell");
+ u3l_log(" first event tail: %s", u3a_is_atom(fst_t) ? "atom" : "cell");
+ }
+ }
// XX check mug if available
//
+ u3l_log("_mars_do_boot: calling u3v_boot(eve)...");
if ( c3n == u3v_boot(eve) ) {
return c3n;
}
@@ -1814,10 +1832,21 @@ _mars_boot_make(u3_boot_opts* inp_u,
// timestamp events, cons list
//
{
+ c3_d bot_c = u3qb_lent(bot);
+ c3_d mod_c = u3qb_lent(mod);
+ c3_d use_c = u3qb_lent(use);
+
+ u3l_log("_mars_boot_make: assembling events");
+ u3l_log(" bot (lifecycle): %llu events (NO timestamp)", (unsigned long long)bot_c);
+ u3l_log(" mod (vanes): %llu events (WITH timestamp)", (unsigned long long)mod_c);
+ u3l_log(" use (userspace): %llu events (WITH timestamp)", (unsigned long long)use_c);
+
u3_noun now = u3_time_in_tv(&inp_u->tim_u);
u3_noun bit = u3qc_bex(48); // 1/2^16 seconds
u3_noun eve = u3kb_flop(bot);
+ u3l_log(" starting with bot events (untimestamped)");
+
{
u3_noun lit = u3kb_weld(mod, use);
u3_noun i, t = lit;
@@ -1832,6 +1861,7 @@ _mars_boot_make(u3_boot_opts* inp_u,
}
*ova = u3kb_flop(eve);
+ u3l_log(" final event list: %llu events", (unsigned long long)u3qb_lent(*ova));
u3z(now); u3z(bit);
}
@@ -1907,6 +1937,8 @@ u3_mars_boot(u3_mars* mar_u, c3_d len_d, c3_y* hun_y)
u3_meta met_u;
u3_noun com, ova, cax;
+ u3l_log("u3_mars_boot()");
+
inp_u.veb_o = __( u3C.wag_w & u3o_verbose );
inp_u.lit_o = c3n; // unimplemented in arvo
diff --git a/vere/pkg/vere/solid_boot_test.c b/vere/pkg/vere/solid_boot_test.c
new file mode 100644
index 0000000..b0d5200
--- /dev/null
+++ b/vere/pkg/vere/solid_boot_test.c
@@ -0,0 +1,178 @@
+/// @file
+/// Test solid pill boot flow - exactly as mars.c does it
+
+#include "vere.h"
+
+static void
+_setup(void)
+{
+ u3m_boot_lite(1 << 28); // 256MB loom
+}
+
+/* Helper wrapper for u3v_boot to use with u3m_soft */
+static u3_noun
+_boot_wrapper(u3_noun ova)
+{
+ if ( c3n == u3v_boot(ova) ) {
+ return u3nc(c3__exit, u3_nul);
+ }
+ return u3nc(u3_blip, u3_nul);
+}
+
+/* _test_solid_boot(): boot from solid.pill following exact mars.c flow
+*/
+static c3_i
+_test_solid_boot(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "═══════════════════════════════════════════════════\n");
+ fprintf(stderr, " Solid Pill Boot Test (Exact C Vere Flow)\n");
+ fprintf(stderr, "═══════════════════════════════════════════════════\n\n");
+
+ /* Step 1: Load solid.pill (like king.c:611) */
+ fprintf(stderr, "[1] Loading solid.pill (u3m_file like king.c)...\n");
+ u3_noun pil_p = u3m_file("solid.pill");
+ // u3_noun pil_p = u3m_file("/home/y/code/urbit/vere/ocaml/solid.pill");
+ u3_noun arv = u3_nul;
+ c3_o arv_atom = u3a_is_atom(arv);
+ c3_o atom2 = u3a_is_atom(42);
+ c3_o atom3 = u3a_is_atom(u3nc(1, 5));
+ u3l_log("arv_atom %u", arv_atom);
+ u3l_log("atoms ? %u - %u", atom2, atom3);
+ u3l_log("c3y %u", c3y);
+ u3l_log("c3n %u", c3n);
+ u3_noun pil2 = u3nc(pil_p, arv);
+
+ fprintf(stderr, " ✓ Loaded pill as atom\n\n");
+
+ /* Step 2: Cue the pill atom (like _mars_sift_pill:1597) */
+ fprintf(stderr, "[2] Cuing pill atom (like _mars_sift_pill)...\n");
+ u3_noun pro = u3m_soft(0, u3ke_cue, u3k(pil_p));
+ u3_noun mot, tag, dat;
+
+ if ( (c3n == u3r_trel(pro, &mot, &tag, &dat))
+ || (u3_blip != mot) )
+ {
+ fprintf(stderr, " ✗ Failed to cue pill\n");
+ u3z(pro);
+ u3z(pil_p);
+ return 0;
+ }
+
+ /* Check for %pill tag */
+ if ( c3__pill != tag ) {
+ fprintf(stderr, " ✗ Not a pill (expected %%pill tag)\n");
+ u3z(pro);
+ u3z(pil_p);
+ return 0;
+ }
+
+ fprintf(stderr, " ✓ Cued successfully, got %%pill\n\n");
+
+ /* Step 3: Parse pill structure [%solid [bot mod use]] */
+ fprintf(stderr, "[3] Parsing pill structure...\n");
+
+ u3_noun sol_tag, events;
+ if ( c3n == u3r_cell(dat, &sol_tag, &events) ) {
+ fprintf(stderr, " ✗ Invalid pill data structure\n");
+ u3z(pro);
+ u3z(pil_p);
+ return 0;
+ }
+
+ /* Extract bot, mod, use */
+ u3_noun bot, mod, use;
+ if ( c3n == u3r_trel(events, &bot, &mod, &use) ) {
+ fprintf(stderr, " ✗ Cannot extract bot/mod/use\n");
+ u3z(pro);
+ u3z(pil_p);
+ return 0;
+ }
+
+ u3k(bot); u3k(mod); u3k(use);
+ u3z(pro);
+ u3z(pil_p);
+
+ c3_d bot_c = u3qb_lent(bot);
+ c3_d mod_c = u3qb_lent(mod);
+ c3_d use_c = u3qb_lent(use);
+
+ fprintf(stderr, " Bot events: %llu\n", (unsigned long long)bot_c);
+ fprintf(stderr, " Mod events: %llu\n", (unsigned long long)mod_c);
+ fprintf(stderr, " Use events: %llu\n", (unsigned long long)use_c);
+ fprintf(stderr, " Total: %llu events\n\n",
+ (unsigned long long)(bot_c + mod_c + use_c));
+
+ /* Step 4: Build event list (like _mars_boot_make lines 1814-1836) */
+ fprintf(stderr, "[4] Building event list (C Vere style)...\n");
+ fprintf(stderr, " Bot events: NOT timestamped\n");
+ fprintf(stderr, " Mod/use events: timestamped\n\n");
+
+ struct timeval tim_u;
+ gettimeofday(&tim_u, 0);
+ u3_noun now = u3_time_in_tv(&tim_u);
+ u3_noun bit = u3qc_bex(48); // 1/2^16 seconds
+ u3_noun eve = u3kb_flop(u3k(bot)); // Bot events WITHOUT timestamp
+
+ {
+ u3_noun lit = u3kb_weld(u3k(mod), u3k(use));
+ u3_noun i, t = lit;
+
+ while ( u3_nul != t ) {
+ u3x_cell(t, &i, &t);
+ now = u3ka_add(now, u3k(bit));
+ eve = u3nc(u3nc(u3k(now), u3k(i)), eve); // WITH timestamp
+ }
+
+ u3z(lit);
+ }
+
+ u3_noun ova = u3kb_flop(eve);
+ u3z(now); u3z(bit);
+ u3z(bot); u3z(mod); u3z(use);
+
+ c3_d eve_count = u3qb_lent(ova);
+ fprintf(stderr, " ✓ Event list built: %llu events\n\n", (unsigned long long)eve_count);
+
+ /* Step 5: Call u3v_boot (like _mars_do_boot line 1160) */
+ fprintf(stderr, "[5] Calling u3v_boot with event list...\n");
+ fprintf(stderr, " (This is what actually happens in C Vere!)\n\n");
+
+ u3_noun boot_pro = u3m_soft(0, _boot_wrapper, ova);
+
+ if ( u3_blip != u3h(boot_pro) ) {
+ fprintf(stderr, " ✗ BOOT FAILED!\n");
+ fprintf(stderr, " Error: ");
+ u3m_p("", u3h(boot_pro));
+ fprintf(stderr, "\n");
+ u3z(boot_pro);
+ return 0;
+ }
+
+ fprintf(stderr, " ✓ BOOT SUCCEEDED!\n");
+ fprintf(stderr, " Kernel mug: %x\n", u3r_mug(u3A->roc));
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "═══════════════════════════════════════════════════\n");
+ fprintf(stderr, " ✓ SOLID PILL BOOT TEST PASSED!\n");
+ fprintf(stderr, "═══════════════════════════════════════════════════\n\n");
+
+ u3z(boot_pro);
+
+ return 1;
+}
+
+int
+main(int argc, char* argv[])
+{
+ _setup();
+
+ if ( !_test_solid_boot() ) {
+ fprintf(stderr, "test solid boot: FAILED\r\n");
+ return 1;
+ }
+
+ u3m_grab(u3_none);
+ fprintf(stderr, "test solid boot: OK\r\n");
+ return 0;
+}
diff --git a/zod/.urb/log/0i0/data.mdb b/zod/.urb/log/0i0/data.mdb
index 3576929..aac7bed 100644
--- a/zod/.urb/log/0i0/data.mdb
+++ b/zod/.urb/log/0i0/data.mdb
Binary files differ
diff --git a/zod/.urb/log/0i0/lock.mdb b/zod/.urb/log/0i0/lock.mdb
index 83e959d..cf0169c 100644
--- a/zod/.urb/log/0i0/lock.mdb
+++ b/zod/.urb/log/0i0/lock.mdb
Binary files differ
diff --git a/zod/.urb/log/0i0/vere.txt b/zod/.urb/log/0i0/vere.txt
index 7c6e483..0455ac0 100644
--- a/zod/.urb/log/0i0/vere.txt
+++ b/zod/.urb/log/0i0/vere.txt
@@ -1 +1 @@
-4.0-0395f1ef05 \ No newline at end of file
+4.0-437df069b5 \ No newline at end of file
diff --git a/zod/.vere.lock b/zod/.vere.lock
index f2694d9..d3bf1bd 100644
--- a/zod/.vere.lock
+++ b/zod/.vere.lock
@@ -1 +1 @@
-502068
+589155