/// Subject model for rune.
/// The subject is a Nox noun containing the entire visible environment.
/// Every name in a rune program resolves through the subject via tree-slot lookup.
///
/// Subject layout (right-nested cons list):
///   [~self [~now [~here [~caps [~code [~libs [~mem ~world]]]]]]]
///
/// Axis assignments (cyber Nox slot navigation):
///   2   = ~self   โ€” neuron identity (@neuron)
///   6   = ~now    โ€” current time (@da)
///   14  = ~here   โ€” current graph location (#)
///   30  = ~caps   โ€” capability set
///   62  = ~code   โ€” the running program core
///   126 = ~libs   โ€” imported library cores
///   254 = ~mem    โ€” persistent state (arbitrary noun)
///   255 = ~world  โ€” cybergraph slice (visible particles and links)
use cyber_hemera as hemera;
use rune_ast::Noun;

/// Hash an address path string to its `~world` key atom.
///
/// This is the single source of truth for address โ†’ key: the lowerer emits this
/// same atom as the scry key for `#path` addresses, so a `~world` built with
/// `path_atom` keys resolves under `look` (opcode 17). Truncates the hemera
/// digest to its first 8 bytes (little-endian).
pub fn path_atom(s: &str) -> u64 {
    let digest = hemera::hash(s.as_bytes());
    u64::from_le_bytes(digest.as_bytes()[..8].try_into().unwrap())
}

/// Build a `~world` graph-slice noun from `(path, value)` entries.
///
/// Layout: `[[key1 val1] [[key2 val2] โ€ฆ 0]]`, where `key = path_atom(path)` โ€”
/// the structure `scry_world` walks. No entries โ†’ `Atom(0)` (empty slice).
pub fn world(entries: &[(&str, Noun)]) -> Noun {
    let mut w = Noun::Atom(0);
    for (path, val) in entries.iter().rev() {
        let entry = Noun::cell(Noun::Atom(path_atom(path)), val.clone());
        w = Noun::cell(entry, w);
    }
    w
}

/// A fully-populated subject noun.
pub struct Subject {
    pub self_neuron: Noun,
    pub now: Noun,
    pub here: Noun,
    pub caps: Noun,
    pub code: Noun,
    pub libs: Noun,
    pub mem: Noun,
    pub world: Noun,
}

impl Subject {
    /// Construct the subject noun: a right-nested cell tree.
    /// Layout: [~self [~now [~here [~caps [~code [~libs [~mem ~world]]]]]]]
    pub fn to_noun(&self) -> Noun {
        // Build from inside out: [mem world], then [libs [mem world]], etc.
        let inner7 = Noun::cell(self.mem.clone(), self.world.clone());
        let inner6 = Noun::cell(self.libs.clone(), inner7);
        let inner5 = Noun::cell(self.code.clone(), inner6);
        let inner4 = Noun::cell(self.caps.clone(), inner5);
        let inner3 = Noun::cell(self.here.clone(), inner4);
        let inner2 = Noun::cell(self.now.clone(), inner3);
        Noun::cell(self.self_neuron.clone(), inner2)
    }

    /// Minimal subject โ€” every slot `Atom(0)`. For pure evaluation that touches
    /// no environment (and for tests).
    pub fn minimal() -> Self {
        Subject {
            self_neuron: Noun::Atom(0),
            now: Noun::Atom(0),
            here: Noun::Atom(0),
            caps: Noun::Atom(0),
            code: Noun::Atom(0),
            libs: Noun::Atom(0),
            mem: Noun::Atom(0),
            world: Noun::Atom(0),
        }
    }

    /// Standalone subject for evaluation outside the cybergraph: a resolvable
    /// `~self` neuron and `~here` home, with the live slots (`~now` clock,
    /// `~world` graph slice) left for the host to fill. The CLI injects a real
    /// `~now`; `~world` stays empty until a graph source (cyb/bbg) is wired.
    pub fn standalone() -> Self {
        Subject {
            self_neuron: Noun::Atom(path_atom("@self")),
            now: Noun::Atom(0),
            here: Noun::Atom(path_atom("~/")),
            caps: Noun::Atom(0),
            code: Noun::Atom(0),
            libs: Noun::Atom(0),
            mem: Noun::Atom(0),
            world: Noun::Atom(0),
        }
    }
}

/// Named slot axes for use in lowering and debugging.
///
/// Subject structure: [s0 [s1 [s2 [s3 [s4 [s5 [s6 s7]]]]]]]
/// Slots 0โ€“6: head at each nesting level โ†’ axis = (1 << (n+2)) - 2
/// Slot 7: tail at the 7th level โ†’ axis 255
pub mod axis {
    pub const SELF:  u64 = 2;
    pub const NOW:   u64 = 6;
    pub const HERE:  u64 = 14;
    pub const CAPS:  u64 = 30;
    pub const CODE:  u64 = 62;
    pub const LIBS:  u64 = 126;
    pub const MEM:   u64 = 254;
    pub const WORLD: u64 = 255;

    /// Axis for the n-th slot in a right-nested cons list (0-indexed).
    /// Slots 0..6: head at each nesting level โ†’ (1 << (n+2)) - 2
    /// Slot 7: tail at the 7th level โ†’ 255
    pub fn subject_slot_axis(n: usize) -> u64 {
        if n < 7 { (1u64 << (n + 2)) - 2 } else { 255 }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rune_interp::axis as nox_axis;

    fn a(n: u64) -> Noun { Noun::Atom(n) }

    /// Build a distinct subject where each slot holds a unique atom,
    /// then verify that each axis constant navigates to the correct value.
    #[test]
    fn axis_constants_are_correct() {
        let subj = Subject {
            self_neuron: a(10),
            now:         a(11),
            here:        a(12),
            caps:        a(13),
            code:        a(14),
            libs:        a(15),
            mem:         a(16),
            world:       a(17),
        };
        let noun = subj.to_noun();

        let get = |ax: u64| nox_axis(&noun, ax).expect("axis failed");

        assert_eq!(get(axis::SELF),  a(10), "SELF axis {}", axis::SELF);
        assert_eq!(get(axis::NOW),   a(11), "NOW axis {}",  axis::NOW);
        assert_eq!(get(axis::HERE),  a(12), "HERE axis {}", axis::HERE);
        assert_eq!(get(axis::CAPS),  a(13), "CAPS axis {}", axis::CAPS);
        assert_eq!(get(axis::CODE),  a(14), "CODE axis {}", axis::CODE);
        assert_eq!(get(axis::LIBS),  a(15), "LIBS axis {}", axis::LIBS);
        assert_eq!(get(axis::MEM),   a(16), "MEM axis {}",  axis::MEM);
        assert_eq!(get(axis::WORLD), a(17), "WORLD axis {}", axis::WORLD);
    }

    #[test]
    fn path_atom_is_stable_and_distinct() {
        assert_eq!(path_atom("cyber/truth"), path_atom("cyber/truth"));
        assert_ne!(path_atom("cyber/truth"), path_atom("cyber/rune"));
        assert_ne!(path_atom("cyber/truth"), 0);
    }

    #[test]
    fn world_keys_are_path_atoms() {
        let w = world(&[("cyber/truth", a(42))]);
        // [[key 42] 0]
        let expected = Noun::cell(
            Noun::cell(a(path_atom("cyber/truth")), a(42)),
            a(0),
        );
        assert_eq!(w, expected);
        assert_eq!(world(&[]), a(0));
    }

    #[test]
    fn standalone_self_resolves_nonzero() {
        let noun = Subject::standalone().to_noun();
        let self_neuron = nox_axis(&noun, axis::SELF).expect("axis");
        assert_ne!(self_neuron, a(0));
    }

    /// subject_slot_axis(n) must agree with the named constants.
    #[test]
    fn subject_slot_axis_matches_constants() {
        assert_eq!(axis::subject_slot_axis(0), axis::SELF);
        assert_eq!(axis::subject_slot_axis(1), axis::NOW);
        assert_eq!(axis::subject_slot_axis(2), axis::HERE);
        assert_eq!(axis::subject_slot_axis(3), axis::CAPS);
        assert_eq!(axis::subject_slot_axis(4), axis::CODE);
        assert_eq!(axis::subject_slot_axis(5), axis::LIBS);
        assert_eq!(axis::subject_slot_axis(6), axis::MEM);
        assert_eq!(axis::subject_slot_axis(7), axis::WORLD);
    }
}

Homonyms

cyb/optica/src/lib.rs
soft3/strata/src/lib.rs
cyb/honeycrisp/src/lib.rs
warriors/trisha/honeycrisp/lib.rs
warriors/trisha/wgpu/lib.rs
soft3/glia/import/lib.rs
soft3/foculus/src/lib.rs
soft3/nox/rs/lib.rs
soft3/cybergraph/src/lib.rs
soft3/tru/rs/lib.rs
soft3/mudra/src/lib.rs
soft3/glia/run/lib.rs
cyb/prysm/rs/lib.rs
warriors/trisha/rs/lib.rs
cyb/src-tauri/src/lib.rs
soft3/mir/src/lib.rs
soft3/lens/src/lib.rs
neural/trident/src/lib.rs
cyb/cyb/cyb-services/src/lib.rs
soft3/strata/nebu/rs/lib.rs
soft3/lens/core/src/lib.rs
neural/rs/mir-format/src/lib.rs
soft3/zheng/rs/src/lib.rs
neural/rune/rs/interp/lib.rs
soft3/radio/iroh-willow/src/lib.rs
neural/rune/rs/parse/lib.rs
neural/eidos/rs/src/lib.rs
neural/rs/darwin-sys/src/lib.rs
soft3/radio/iroh-gossip/src/lib.rs
soft3/radio/iroh-ffi/src/lib.rs
soft3/radio/iroh-car/src/lib.rs
soft3/radio/iroh-relay/src/lib.rs
soft3/bbg/rs/src/lib.rs
soft3/radio/iroh-docs/src/lib.rs
soft3/lens/ikat/src/lib.rs
neural/rune/rs/lex/lib.rs
cyb/honeycrisp/aruminium/src/lib.rs
soft3/hemera/rs/src/lib.rs
neural/rune/rs/ast/lib.rs
soft3/radio/iroh-blobs/src/lib.rs
cyb/honeycrisp/acpu/src/lib.rs
soft3/lens/porphyry/src/lib.rs
cyb/honeycrisp/rane/src/lib.rs
neural/rune/rs/compile/lib.rs
neural/rune/rs/parse-pure/lib.rs
neural/rs/codegen/src/lib.rs
soft3/lens/binius/src/lib.rs
neural/rune/rs/prysm/lib.rs
neural/rs/link/src/lib.rs
neural/rune/rs/mold/lib.rs
soft3/strata/proof/src/lib.rs
soft3/lens/brakedown/src/lib.rs
soft3/strata/kuro/rs/lib.rs
soft3/lens/assayer/src/lib.rs
neural/rs/core/src/lib.rs
neural/rs/macros/src/lib.rs
soft3/radio/cyber-bao/src/lib.rs
soft3/strata/compute/src/lib.rs
soft3/radio/iroh-base/src/lib.rs
soft3/radio/iroh-dns-server/src/lib.rs
neural/rune/rs/lower/lib.rs
soft3/strata/ext/src/lib.rs
soft3/strata/core/src/lib.rs
soft3/hemera/wgsl/src/lib.rs
soft3/radio/iroh/src/lib.rs
cyb/honeycrisp/unimem/src/lib.rs
cyb/evy/crates/evy_engine_tasks/src/lib.rs
cyb/evy/crates/evy_dialect/src/lib.rs
cyb/wysm/crates/wasi/src/lib.rs
cyb/wysm/crates/fuzz/src/lib.rs
soft3/strata/genies/rs/src/lib.rs
cyb/evy/crates/evy_platform_caps/src/lib.rs
neural/inf/rs/oracle/src/lib.rs
soft3/strata/jali/wgsl/src/lib.rs
cyb/evy/forks/bevy_transform/src/lib.rs
soft3/tape/impl/rust/src/lib.rs
cyb/wysm/crates/wasmi/src/lib.rs
cyb/evy/forks/bevy_render/src/lib.rs
cyb/evy/crates/evy_ecs_storage/src/lib.rs
cyb/evy/forks/naga/src/lib.rs
soft3/strata/trop/wgsl/src/lib.rs
cyb/wysm/crates/c_api/artifact/lib.rs
cyb/evy/forks/bevy_ecs/src/lib.rs
cyb/wysm/crates/ir/src/lib.rs
cyb/evy/forks/bevy_animation/src/lib.rs
cyb/evy/forks/bevy_sprite_render/src/lib.rs
cyb/wysm/crates/c_api/src/lib.rs
neural/inf/rs/parse/src/lib.rs
soft3/strata/trop/rs/src/lib.rs
soft3/strata/kuro/wgsl/src/lib.rs
neural/trident/editor/zed/src/lib.rs
cyb/evy/forks/bevy_mesh/src/lib.rs
cyb/evy/crates/evy_radio/src/lib.rs
cyb/evy/forks/bevy_anti_alias/src/lib.rs
soft3/strata/jali/rs/src/lib.rs
cyb/wysm/crates/wast/src/lib.rs
neural/inf/rs/plan/src/lib.rs
neural/rs/tests/macro-integration/src/lib.rs
soft3/radio/iroh-ffi/iroh-js/src/lib.rs
cyb/evy/forks/bevy_image/src/lib.rs
cyb/evy/forks/bevy_post_process/src/lib.rs
neural/inf/rs/source/src/lib.rs
cyb/wysm/crates/core/src/lib.rs
cyb/evy/crates/evy_diagnostic/src/lib.rs
cyb/evy/crates/evy_engine_dispatch/src/lib.rs
cyb/evy/forks/bevy_pbr/src/lib.rs
cyb/evy/forks/bevy_gizmos/src/lib.rs
cyb/evy/forks/bevy_gizmos_render/src/lib.rs
soft3/radio/iroh/bench/src/lib.rs
neural/inf/rs/lex/src/lib.rs
neural/inf/rs/ast/src/lib.rs
soft3/strata/genies/wgsl/src/lib.rs
soft3/strata/nebu/wgsl/src/lib.rs
cyb/wysm/crates/collections/src/lib.rs
neural/inf/rs/lower/src/lib.rs
cyb/evy/forks/bevy_sprite/src/lib.rs
cyb/evy/forks/bevy_diagnostic/src/lib.rs
neural/inf/rs/eval/src/lib.rs
cyb/wysm/crates/c_api/macro/lib.rs
cyb/evy/forks/bevy_tasks/src/lib.rs
cyb/evy/forks/bevy_core_pipeline/src/lib.rs
cyb/evy/crates/evy_prysm_core/src/lib.rs
neural/inf/rs/value/src/lib.rs
cyb/evy/crates/evy_engine_core/src/lib.rs
soft3/radio/tests/integration/src/lib.rs
bootloader/go-cyber/cw/packages/cyber-std-test/src/lib.rs
bootloader/go-cyber/cw/contracts/std-test/src/lib.rs
bootloader/go-cyber/cw/contracts/graph-filter/src/lib.rs
bootloader/go-cyber/cw/packages/cyber-std/src/lib.rs

Graph