fix(os): cover full spec behavior-descriptor set (PDF §524-525)

cb.rs previously implemented only 3 of the 6 spec descriptors (pure/io/hot).
PDF §524-525 lists: pure function, I/O heavy, allocates memory, touches
network, hot path, security sensitive.

- Behavior now stores descriptor bits directly as dedicated header-flag bits
  in the spare 8..=15 range: PURE=9, IO_HEAVY=10, ALLOCATES=11, NETWORK=13,
  HOT_PATH=14, SECURITY_SENSITIVE=15. Bit 12 (ENCRYPTED) left clear; mask
  0xEE00. refresh_flags() preserves 8..=15 so round-trip is safe.
- K= CLI tokens gain alloc/net/sec (prog + kernel verbs).
- Add unit test for all-six round-trip + explicit security-sensitive bit.

Proven green via ./check quick.
This commit is contained in:
CUBELinux-2
2026-08-13 16:00:34 -04:00
parent 1539ecd1bb
commit 0074f3112c
2 changed files with 83 additions and 38 deletions
+77 -38
View File
@@ -5,9 +5,16 @@
//! discussion at PDF §524–525), functions/operators that live in CUBE are:
//! * addressed in a reserved `C` axis band (here `c210..=c219`),
//! * tagged with a `kind` (`fn`/`kernel`/`layer`/`checkpoint`/`variant`), and
//! * annotated with *behavior descriptor* flags (pure / I/O heavy /
//! allocates / touches-net / hot-path) carried in the record header's
//! out-of-band flag bits (tag 12, see cubestore encode/decode).
//! * annotated with *behavior descriptor* flags carried in the record
//! header's out-of-band flag bits (tag 12, see cubestore encode/decode).
//!
//! The full spec-derived descriptor set (PDF §524–525) is: "pure function,"
//! "I/O heavy," "allocates memory," "touches network," "hot path," and
//! "security sensitive." We store each directly as a dedicated header-flag bit
//! inside the spare 8..=15 range, deliberately leaving bit 12
//! (`cubecrypt::HEADER_FLAG_ENCRYPTED`) clear. `CubeHeader::refresh_flags()`
//! preserves spare bits 8..=15, so a header carrying descriptors survives an
//! encode→decode→refresh round-trip.
//!
//! This is deliberately NOT a store-IO model: the cubevm never reads or writes
//! OS state records directly. The OS's *decisions/computation* live as CUBE
@@ -24,49 +31,48 @@ pub const C_OS_KERNEL: u8 = 210;
/// decides an effect (kept distinct from the compute-kernel band above).
pub const C_OS_EFFECT: u8 = 211;
/// Header-flag bits 13..=15 are reserved for the behavior-descriptor field.
/// (Bit 12 is `cubecrypt::HEADER_FLAG_ENCRYPTED` and must stay clear of this
/// mask.) `HEADER_FLAG_BEHAVIOR` therefore spans exactly 0b1110_0000_0000_0000
/// (0xE000). `refresh_flags()` preserves spare bits 8..=15, so a header
/// carrying a descriptor survives an encode→decode→refresh round-trip.
pub const HEADER_FLAG_BEHAVIOR: u16 = 0b1110_0000_0000_0000; // bits 13,14,15
/// Header-flag bits reserved for the behavior-descriptor field. Six spec
/// descriptors, each a dedicated bit in the spare 8..=15 range, leaving bit 12
/// (`cubecrypt::HEADER_FLAG_ENCRYPTED`) untouched:
/// PURE=9, IO_HEAVY=10, ALLOCATES=11, NETWORK=13, HOT_PATH=14, SECURITY=15.
pub const HEADER_FLAG_BEHAVIOR: u16 =
0b1110_1110_0000_0000; // bits 9,10,11,13,14,15 (bit12 reserved)
/// Bit position of the behavior-descriptor field within the raw flag word.
pub const BEHAVIOR_SHIFT: u16 = 13;
/// Behavior descriptors for an OS operator kernel (PDF §524–525).
/// Behavior descriptors for an OS operator kernel (PDF §524–525, full set).
///
/// These are a closed, spec-derived set: "pure function," "I/O heavy,"
/// "allocates memory," "touches network," "hot path." We map them onto three
/// available out-of-band bits (13..=15) and add `Variant` (an alternate
/// implementation, one of the PDF's `Kind`s used as a descriptor tag) because
/// the OS kernel graph benefits from marking experimental variants. The set is
/// intentionally tiny and mirrors the PDF's examples rather than inventing new
/// semantics.
/// Each variant is stored directly as its dedicated header-flag bit (subset of
/// `HEADER_FLAG_BEHAVIOR`), so `Behavior` carries the raw descriptor bits and
/// round-trips through the header codec without bit-shift collisions.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Behavior(pub u8);
pub struct Behavior(pub u16);
impl Behavior {
/// Pure function: no side effects, deterministic on inputs.
pub const PURE: u8 = 1 << 0;
pub const PURE: u16 = 1 << 9; // 0x0200
/// I/O heavy: performs significant store/device I/O.
pub const IO_HEAVY: u8 = 1 << 1;
pub const IO_HEAVY: u16 = 1 << 10; // 0x0400
/// Allocates memory: grows the heap / maps pages.
pub const ALLOCATES: u16 = 1 << 11; // 0x0800
/// Touches network: performs socket/link I/O (security-relevant surface).
pub const NETWORK: u16 = 1 << 13; // 0x2000
/// Hot path: executed frequently; a candidate for optimization/variant swap.
pub const HOT_PATH: u8 = 1 << 2;
pub const HOT_PATH: u16 = 1 << 14; // 0x4000
/// Security sensitive: elevated privilege / trust boundary crossing.
pub const SECURITY_SENSITIVE: u16 = 1 << 15; // 0x8000
/// Build from a raw 3-bit value (already shifted into bit 13..=15 space).
pub fn from_raw(bits: u8) -> Self {
Behavior(bits & 0b111)
/// Combine descriptor bits (e.g. `Behavior::PURE | Behavior::HOT_PATH`).
pub fn new(bits: u16) -> Self {
Behavior(bits & HEADER_FLAG_BEHAVIOR)
}
/// Encode into the out-of-band header flag bits (bits 13..=15).
/// Encode into the out-of-band header flag bits.
pub fn to_flags(self) -> u16 {
((self.0 & 0b111) as u16) << BEHAVIOR_SHIFT
self.0 & HEADER_FLAG_BEHAVIOR
}
/// Decode from a raw 16-bit flag word (reads bits 13..=15).
/// Decode from a raw 16-bit flag word (keeps only the behavior bits).
pub fn from_flags(flags: u16) -> Self {
Behavior(((flags & HEADER_FLAG_BEHAVIOR) >> BEHAVIOR_SHIFT) as u8)
Behavior(flags & HEADER_FLAG_BEHAVIOR)
}
/// True if any descriptor bit is set.
@@ -83,9 +89,18 @@ impl Behavior {
if self.0 & Self::IO_HEAVY != 0 {
out.push("io");
}
if self.0 & Self::ALLOCATES != 0 {
out.push("alloc");
}
if self.0 & Self::NETWORK != 0 {
out.push("net");
}
if self.0 & Self::HOT_PATH != 0 {
out.push("hot");
}
if self.0 & Self::SECURITY_SENSITIVE != 0 {
out.push("sec");
}
out
}
}
@@ -98,12 +113,17 @@ mod tests {
#[test]
fn behavior_round_trips_through_store() {
// PURE | HOT_PATH must survive the real store round-trip (what
// native-apply reads back). This is the exact field the effector
// inspects, and the gate caught it dropping the HOT_PATH bit.
let b = Behavior(Behavior::PURE | Behavior::HOT_PATH);
// All six spec descriptors must survive the real store round-trip
// (what native-apply reads back). This is the exact field the
// effector inspects; an earlier 3-bit mask (0x7000) silently dropped
// HOT_PATH (bit 15) and collided with ENCRYPTED (bit 12).
let b = Behavior::new(
Behavior::PURE | Behavior::IO_HEAVY | Behavior::ALLOCATES | Behavior::NETWORK,
);
let raw = b.to_flags();
assert_eq!(raw, 0x2000 | 0x8000, "to_flags wrong: {raw:#x}");
assert_eq!(raw, 0x0200 | 0x0400 | 0x0800 | 0x2000, "to_flags wrong: {raw:#x}");
// ENCRYPTED bit (12) must never be set by a descriptor.
assert_eq!(raw & (1 << 12), 0, "descriptor must not set ENCRYPTED bit");
let mut store = CubeStore::new(HashBackend::new());
let coord = Czyx::new(210, 1, 1, 3);
@@ -118,9 +138,28 @@ mod tests {
let back = Behavior::from_flags(read_h.flags.bits());
assert_eq!(
back, b,
"behavior dropped on store round-trip: stored {raw:#x}, got {:#x} (bits {:#x})",
read_h.flags.bits(),
"behavior dropped on store round-trip: stored {raw:#x}, got {:#x}",
read_h.flags.bits()
);
}
#[test]
fn security_sensitive_bit_used_and_round_trips() {
// Explicitly cover the 6th spec descriptor (security sensitive, bit15).
let b = Behavior::new(Behavior::SECURITY_SENSITIVE | Behavior::HOT_PATH);
let mut store = CubeStore::new(HashBackend::new());
let coord = Czyx::new(210, 1, 1, 5);
let mut h = CubeHeader::new();
h.doc_type = Some("kernel".into());
h.title = Some("privileged-op".into());
h.flags.0 |= b.to_flags();
h.refresh_flags();
store.put_record(coord, &h, &[]);
let (read_h, _) = store.get_record(&coord).expect("record present");
assert_eq!(
Behavior::from_flags(read_h.flags.bits()),
b,
"security-sensitive descriptor lost on round-trip"
);
}
}
+6
View File
@@ -461,6 +461,9 @@ impl Session {
let bit = match flag {
"pure" => cubecode::Behavior::PURE,
"io" => cubecode::Behavior::IO_HEAVY,
"alloc" => cubecode::Behavior::ALLOCATES,
"net" => cubecode::Behavior::NETWORK,
"sec" => cubecode::Behavior::SECURITY_SENSITIVE,
"hot" => cubecode::Behavior::HOT_PATH,
other => return Err(format!("prog: unknown descriptor K={other}")),
};
@@ -591,6 +594,9 @@ impl Session {
let bit = match flag {
"pure" => cubecode::Behavior::PURE,
"io" => cubecode::Behavior::IO_HEAVY,
"alloc" => cubecode::Behavior::ALLOCATES,
"net" => cubecode::Behavior::NETWORK,
"sec" => cubecode::Behavior::SECURITY_SENSITIVE,
"hot" => cubecode::Behavior::HOT_PATH,
other => return Err(format!("kernel: unknown descriptor K={other}")),
};