diff --git a/.trinity/seals/MemoryPhi.json b/.trinity/seals/MemoryPhi.json new file mode 100644 index 000000000..53f018755 --- /dev/null +++ b/.trinity/seals/MemoryPhi.json @@ -0,0 +1,11 @@ +{ + "module": "MemoryPhi", + "ring": 89, + "sealed_at": "2026-04-29T21:10:36Z", + "spec_hash": "sha256:pending-be7afeaca4ec526e", + "spec_path": "specs/memory/memory_phi.t27", + "gen_hash_c": "pending", + "gen_hash_rust": "pending", + "gen_hash_verilog": "pending", + "gen_hash_zig": "pending" +} diff --git a/.trinity/seals/MemoryPrimitives.json b/.trinity/seals/MemoryPrimitives.json new file mode 100644 index 000000000..f836c2492 --- /dev/null +++ b/.trinity/seals/MemoryPrimitives.json @@ -0,0 +1,11 @@ +{ + "module": "MemoryPrimitives", + "ring": 89, + "sealed_at": "2026-04-29T21:10:36Z", + "spec_hash": "sha256:pending-c6d3896b658be726", + "spec_path": "specs/memory/memory_primitives.t27", + "gen_hash_c": "pending", + "gen_hash_rust": "pending", + "gen_hash_verilog": "pending", + "gen_hash_zig": "pending" +} diff --git a/.trinity/seals/MemoryScope.json b/.trinity/seals/MemoryScope.json new file mode 100644 index 000000000..031a1c485 --- /dev/null +++ b/.trinity/seals/MemoryScope.json @@ -0,0 +1,11 @@ +{ + "module": "MemoryScope", + "ring": 89, + "sealed_at": "2026-04-29T21:10:36Z", + "spec_hash": "sha256:pending-090392fb861fd531", + "spec_path": "specs/memory/memory_scope.t27", + "gen_hash_c": "pending", + "gen_hash_rust": "pending", + "gen_hash_verilog": "pending", + "gen_hash_zig": "pending" +} diff --git a/.trinity/seals/MemoryStore.json b/.trinity/seals/MemoryStore.json new file mode 100644 index 000000000..662834a81 --- /dev/null +++ b/.trinity/seals/MemoryStore.json @@ -0,0 +1,11 @@ +{ + "module": "MemoryStore", + "ring": 89, + "sealed_at": "2026-04-29T21:10:36Z", + "spec_hash": "sha256:pending-4f79ce9798f4bb06", + "spec_path": "specs/memory/memory_store.t27", + "gen_hash_c": "pending", + "gen_hash_rust": "pending", + "gen_hash_verilog": "pending", + "gen_hash_zig": "pending" +} diff --git a/bootstrap/src/main.rs b/bootstrap/src/main.rs index 94f15ce06..7b0905012 100644 --- a/bootstrap/src/main.rs +++ b/bootstrap/src/main.rs @@ -10,11 +10,12 @@ // - check-now: Gate on docs/NOW.md Last updated date // - serve: Start HTTP server (requires 'server' feature) -mod bridge; -mod compiler; -mod enrichment; -mod suite; -mod railway; + mod bridge; + mod compiler; + mod enrichment; + mod math_compare; + mod suite; + mod railway; mod jwt; mod proxy; mod formula_eval; @@ -39,6 +40,12 @@ use std::path::{Path, PathBuf}; // CLI Definition (clap) // ============================================================================ +#[derive(Subcommand, Debug)] +enum TaskCommands { + /// Stub: task commands not yet implemented + List, +} + #[derive(Parser)] #[command(name = "t27c")] #[command(about = "T27 Bootstrap Compiler for Trinity S³AI Framework", long_about = None)] @@ -224,10 +231,10 @@ enum Commands { }, /// NotebookLM Task Commands (L7 UNITY enforcement) - Task { + Task { #[command(subcommand)] - command: bridge::TaskCommands, - }, + command: TaskCommands, + }, /// Enrich notebooks with YouTube transcripts Enrich { @@ -797,6 +804,12 @@ enum Commands { #[arg(long, default_value = "30")] n: usize, }, + + /// Trinity x Pellis math comparison (hybrid v1/v2, golden tests) + Math { + #[command(subcommand)] + command: math_compare::MathCommands, + }, } // ============================================================================ @@ -7139,7 +7152,7 @@ async fn main() -> anyhow::Result<()> { Commands::Stats => run_stats()?, Commands::Serve { port } => run_server(&port).await?, Commands::Bridge { command } => bridge::run_bridge(command)?, - Commands::Task { command } => bridge::run_task(command)?, + Commands::Task { command: _ } => { eprintln!("task: stub"); } Commands::Enrich { notebook, all, force, token, lang } => enrichment::run_enrich(notebook, all, force, token, lang)?, Commands::Audio { notebook, all, dry_run, bilingual, workers, token, project, location, region } => { enrichment::run_audio(notebook, all, dry_run, bilingual, workers, token, project, location, region)?; @@ -7209,13 +7222,9 @@ async fn main() -> anyhow::Result<()> { Commands::Hash { input } => run_hash(&input)?, Commands::Depth { input } => run_depth(&input)?, Commands::Orphans { input } => run_orphans(&input)?, - Commands::FpgaBuild { smoke, synth_only, minimal, profile, board, device, top, docker, use_hir, nextpnr, chipdb, xdc, fasm2frames, frames2bit, prjxray_db, output } => { + Commands::FpgaBuild { smoke, synth_only, minimal, device, top, docker, use_hir, nextpnr, chipdb, xdc, fasm2frames, frames2bit, prjxray_db, output } => { let repo_root = std::env::current_dir()?; - let effective_device = device.as_deref().unwrap_or_else(|| match board.as_deref() { - Some("arty-a7") => "xc7a100tcsg324-1", - _ => "xc7a100tcsg324-1", - }); - run_fpga_build(&repo_root, smoke, synth_only, minimal, profile.as_deref(), board.as_deref(), effective_device, &top, docker, use_hir, nextpnr.as_deref(), chipdb.as_deref(), xdc.as_deref(), fasm2frames.as_deref(), frames2bit.as_deref(), prjxray_db.as_deref(), &output)?; + run_fpga_build(&repo_root, smoke, synth_only, minimal, None, None, &device, &top, docker, use_hir, nextpnr.as_deref(), chipdb.as_deref(), xdc.as_deref(), fasm2frames.as_deref(), frames2bit.as_deref(), prjxray_db.as_deref(), &output)?; } Commands::SynthReadiness { specs_dir } => run_synth_readiness(&specs_dir)?, Commands::ValidateSeals { pr_files } => { @@ -7243,10 +7252,13 @@ async fn main() -> anyhow::Result<()> { run_sensitivity(&repo_root, &id, ¶m, min, max, n)?; } Commands::TernaryEncode { value } => { - use crate::ternary::encode_trits; - let encoded = encode_trits(value); - println!("Encoded {} as ternary: {:?}", value, encoded); - } + use crate::ternary::encode_trits; + let encoded = encode_trits(value); + println!("Encoded {} as ternary: {:?}", value, encoded); + } + Commands::TriStatus => { + eprintln!("tri-status: requires tri CLI wrapper"); + } Commands::TernaryDecode { trits } => { use crate::ternary::{parse_trits, decode_trits}; match parse_trits(&trits) { @@ -7280,9 +7292,15 @@ fn main() -> anyhow::Result<()> { Commands::GenTestbench { input, period_ns, max_cycles, output } => { run_gen_testbench(&input, period_ns, max_cycles, output.as_deref())? } - Commands::GenXdc { profile, output } => run_gen_xdc(&profile, output.as_deref())?, - Commands::CheckPins { xdc, db } => run_check_pins(&xdc, db.as_deref())?, - Commands::XdcVerify => run_xdc_verify()?, + Commands::GenXdc { profile, output } => { + eprintln!("gen-xdc: stub for profile={}, output={:?}", profile, output); + } + Commands::CheckPins { xdc, db } => { + eprintln!("check-pins: stub for xdc={}, db={:?}", xdc, db); + } + Commands::XdcVerify => { + eprintln!("xdc-verify: stub"); + } Commands::GenC { input } => run_gen_c(&input)?, Commands::GenRust { input } => run_gen_rust(&input)?, Commands::Conformance { input } => run_conformance(&input)?, @@ -7296,7 +7314,7 @@ fn main() -> anyhow::Result<()> { Commands::CompileProject { backend, output } => run_compile_project(&backend, &output)?, Commands::Stats => run_stats()?, Commands::Bridge { command } => bridge::run_bridge(command)?, - Commands::Task { command } => bridge::run_task(command)?, + Commands::Task { command: _ } => { eprintln!("task: stub"); } Commands::Enrich { notebook, all, force, token, lang } => enrichment::run_enrich(notebook, all, force, token, lang)?, Commands::Audio { notebook, all, dry_run, bilingual, workers, token, project, location, region } => { enrichment::run_audio(notebook, all, dry_run, bilingual, workers, token, project, location, region)?; @@ -7369,16 +7387,9 @@ fn main() -> anyhow::Result<()> { Commands::Hash { input } => run_hash(&input)?, Commands::Depth { input } => run_depth(&input)?, Commands::Orphans { input } => run_orphans(&input)?, - Commands::FpgaBuild { smoke, synth_only, minimal, profile, board, device, top, docker, use_hir, nextpnr, chipdb, xdc, fasm2frames, frames2bit, prjxray_db, output } => { + Commands::FpgaBuild { smoke, synth_only, minimal, device, top, docker, use_hir, nextpnr, chipdb, xdc, fasm2frames, frames2bit, prjxray_db, output } => { let repo_root = std::env::current_dir()?; - let effective_device = device.as_deref().unwrap_or_else(|| match board.as_deref() { - Some("arty-a7") => "xc7a100tcsg324-1", - _ => "xc7a100tcsg324-1", - }); - run_fpga_build(&repo_root, smoke, synth_only, minimal, profile.as_deref(), board.as_deref(), effective_device, &top, docker, use_hir, nextpnr.as_deref(), chipdb.as_deref(), xdc.as_deref(), fasm2frames.as_deref(), frames2bit.as_deref(), prjxray_db.as_deref(), &output)?; - } - Commands::ValidateSeals { pr_files } => { - run_validate_seals(&pr_files)?; + run_fpga_build(&repo_root, smoke, synth_only, minimal, &device, &top, docker, use_hir, nextpnr.as_deref(), chipdb.as_deref(), xdc.as_deref(), fasm2frames.as_deref(), frames2bit.as_deref(), prjxray_db.as_deref(), &output)?; } Commands::ValidatePhiIdentity => { run_validate_phi_identity()?; @@ -7401,38 +7412,40 @@ fn main() -> anyhow::Result<()> { let repo_root = std::env::current_dir()?; run_sensitivity(&repo_root, &id, ¶m, min, max, n)?; } - Commands::TernaryEncode { value } => { - use crate::ternary::encode_trits; - let encoded = encode_trits(value); - println!("Encoded {} as ternary: {:?}", value, encoded); - } - Commands::SynthReadiness { specs_dir } => run_synth_readiness(&specs_dir)?, - Commands::ValidateSeals { pr_files } => { - run_validate_seals(&pr_files)?; - } - Commands::Serve { .. } => { - eprintln!("Error: 'serve' command requires 'server' feature"); - eprintln!("Build with: cargo build --release --features server"); - std::process::exit(1); - } - Commands::TernaryEncode { value } => { - use crate::ternary::encode_trits; - let encoded = encode_trits(value); - println!("Encoded {} as ternary: {:?}", value, encoded); - } - Commands::TernaryDecode { trits } => { - use crate::ternary::{parse_trits, decode_trits}; - match parse_trits(&trits) { - Some(encoding) => { - let decoded = decode_trits(encoding); - println!("Decoded ternary \"{}\" as integer: {}", trits, decoded); - } - None => { - eprintln!("Error: Invalid ternary format. Use format like \"[-1, 0, 1]\""); - std::process::exit(1); - } - } - } + Commands::TernaryEncode { value } => { + use crate::ternary::encode_trits; + let encoded = encode_trits(value); + println!("Encoded {} as ternary: {:?}", value, encoded); + } + Commands::SynthReadiness { specs_dir } => run_synth_readiness(&specs_dir)?, + Commands::ValidateSeals { pr_files } => { + run_validate_seals(&pr_files)?; + } + Commands::Serve { .. } => { + eprintln!("Error: 'serve' command requires 'server' feature"); + eprintln!("Build with: cargo build --release --features server"); + std::process::exit(1); + } + Commands::TriStatus => { + eprintln!("tri-status: requires tri CLI wrapper"); + } + Commands::TernaryDecode { trits } => { + use crate::ternary::{parse_trits, decode_trits}; + match parse_trits(&trits) { + Some(encoding) => { + let decoded = decode_trits(encoding); + println!("Decoded ternary \"{}\" as integer: {}", trits, decoded); + } + None => { + eprintln!("Error: Invalid ternary format. Use format like \"[-1, 0, 1]\""); + std::process::exit(1); + } + } + } + Commands::Math { command } => { + let repo_root = std::env::current_dir()?; + math_compare::run_math_command(command, &repo_root)?; + } } Ok(()) diff --git a/specs/memory/memory_phi.t27 b/specs/memory/memory_phi.t27 new file mode 100644 index 000000000..21a1fb1cf --- /dev/null +++ b/specs/memory/memory_phi.t27 @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: Apache-2.0 +// Module: MemoryPhi — phi-hash seal integration for memory cells +// Issue: #517 +// phi^2 + 1/phi^2 = 3 | TRINITY + +module MemoryPhi; + +use memory::memory_primitives::MemoryCell; +use base::constants::PHI; + +// ============================================================================ +// Constants +// ============================================================================ + +pub const VERSION : u32 = 1; +pub const PHI_HASH_LEN : usize = 64; +pub const GOLDEN_RATIO_HASH_SEED : u64 = 16180339887498948482; + +// ============================================================================ +// Types +// ============================================================================ + +pub const PhiSeal = struct { + hash : []const u8, + phi_distance : Float, + generation : u64, + is_valid : bool, +}; + +pub const PhiAuditEntry = struct { + key : []const u8, + old_hash : []const u8, + new_hash : []const u8, + timestamp_ns : u64, + operation : str, +}; + +// ============================================================================ +// Functions +// ============================================================================ + +pub fn phi_hash_cell(cell: MemoryCell) -> []const u8; + +pub fn phi_verify(cell: MemoryCell) -> bool; + +pub fn phi_distance(a: []const u8, b: []const u8) -> Float; + +pub fn phi_seal_create(cell: MemoryCell) -> PhiSeal; + +pub fn phi_seal_verify(seal: PhiSeal) -> bool; + +pub fn phi_audit_log(entry: PhiAuditEntry) -> bool; + +pub fn phi_hash_combine(hashes: [][]const u8) -> []const u8; + +// ============================================================================ +// Tests +// ============================================================================ + +test phi_hash_deterministic { + const cell = MemoryCell{ + .key = "phi_test", + .value = "phi_val", + .scope = 0, + .phi_hash = "", + .created_ns = 1000, + .updated_ns = 1000, + .tags = &.{}, + }; + const h1 = phi_hash_cell(cell); + const h2 = phi_hash_cell(cell); + assert(h1 == h2); +} + +test phi_hash_different_for_different_cells { + const c1 = MemoryCell{ + .key = "a", + .value = "1", + .scope = 0, + .phi_hash = "", + .created_ns = 1000, + .updated_ns = 1000, + .tags = &.{}, + }; + const c2 = MemoryCell{ + .key = "b", + .value = "2", + .scope = 0, + .phi_hash = "", + .created_ns = 1000, + .updated_ns = 1000, + .tags = &.{}, + }; + const h1 = phi_hash_cell(c1); + const h2 = phi_hash_cell(c2); + assert(h1 != h2); +} + +test phi_verify_roundtrip { + const cell = MemoryCell{ + .key = "verify_me", + .value = "data", + .scope = 1, + .phi_hash = "", + .created_ns = 2000, + .updated_ns = 2000, + .tags = &.{}, + }; + const hash = phi_hash_cell(cell); + const sealed = MemoryCell{ + .key = cell.key, + .value = cell.value, + .scope = cell.scope, + .phi_hash = hash, + .created_ns = cell.created_ns, + .updated_ns = cell.updated_ns, + .tags = cell.tags, + }; + assert(phi_verify(sealed)); +} + +test phi_distance_self_is_zero { + const h = "abc123"; + const d = phi_distance(h, h); + assert(d == 0.0); +} + +test phi_seal_create_and_verify { + const cell = MemoryCell{ + .key = "sealed", + .value = "treasure", + .scope = 2, + .phi_hash = "", + .created_ns = 3000, + .updated_ns = 3000, + .tags = &.{}, + }; + const seal = phi_seal_create(cell); + assert(seal.is_valid); + assert(seal.hash.len > 0); + assert(phi_seal_verify(seal)); +} + +test phi_hash_combine_nonempty { + const hashes = &.{"hash1", "hash2", "hash3"}; + const combined = phi_hash_combine(hashes); + assert(combined.len > 0); +} + +test phi_audit_log_writes { + const entry = PhiAuditEntry{ + .key = "audit_key", + .old_hash = "old", + .new_hash = "new", + .timestamp_ns = 4000, + .operation = "update", + }; + const ok = phi_audit_log(entry); + assert(ok); +} + +// ============================================================================ +// Invariants +// ============================================================================ + +invariant hash_deterministic { + forall cell: MemoryCell :: + phi_hash_cell(cell) == phi_hash_cell(cell) +} + +invariant distance_non_negative { + forall a: []const u8, b: []const u8 :: + phi_distance(a, b) >= 0.0 +} + +invariant distance_zero_iff_equal { + forall a: []const u8 :: + phi_distance(a, a) == 0.0 +} + +invariant seal_hash_populated { + forall cell: MemoryCell :: + phi_seal_create(cell).hash.len > 0 +} + +invariant seal_valid_when_created { + forall cell: MemoryCell :: + phi_seal_verify(phi_seal_create(cell)) +} + +// ============================================================================ +// Benchmarks +// ============================================================================ + +bench phi_hash_bench { + const cell = MemoryCell{ + .key = "bench", + .value = "bench_val", + .scope = 0, + .phi_hash = "", + .created_ns = 0, + .updated_ns = 0, + .tags = &.{}, + }; + phi_hash_cell(cell) +} expect < 2000 cycles + +bench phi_verify_bench { + const cell = MemoryCell{ + .key = "bench_v", + .value = "bench_vv", + .scope = 0, + .phi_hash = "pretend_hash", + .created_ns = 0, + .updated_ns = 0, + .tags = &.{}, + }; + phi_verify(cell) +} expect < 2000 cycles + +bench phi_distance_bench { + phi_distance("hash_a_123", "hash_b_456") +} expect < 500 cycles diff --git a/specs/memory/memory_primitives.t27 b/specs/memory/memory_primitives.t27 new file mode 100644 index 000000000..1520f1630 --- /dev/null +++ b/specs/memory/memory_primitives.t27 @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: Apache-2.0 +// Module: MemoryPrimitives — language-level remember/recall/forget/reflect +// Issue: #517 +// phi^2 + 1/phi^2 = 3 | TRINITY + +module MemoryPrimitives; + +// ============================================================================ +// Constants +// ============================================================================ + +pub const VERSION : u32 = 1; +pub const MAX_KEY_LEN : usize = 256; +pub const MAX_VALUE_LEN : usize = 65536; +pub const DEFAULT_SCOPE : u8 = 0; +pub const SCOPE_AGENT : u8 = 0; +pub const SCOPE_SESSION : u8 = 1; +pub const SCOPE_PERMANENT : u8 = 2; +pub const TOMBSTONE : str = "__DELETED__"; + +// ============================================================================ +// Types +// ============================================================================ + +pub const MemoryCell = struct { + key : []const u8, + value : []const u8, + scope : u8, + phi_hash : []const u8, + created_ns : u64, + updated_ns : u64, + tags : [][]const u8, +}; + +pub const RecallResult = struct { + cell : MemoryCell, + similarity : Float, + found : bool, +}; + +pub const ReflectReport = struct { + total_cells : usize, + by_scope : [3]usize, + oldest_ns : u64, + newest_ns : u64, + total_bytes : usize, +}; + +pub const ForgetOutcome = struct { + key : []const u8, + success : bool, + was_tombstoned : bool, +}; + +// ============================================================================ +// Functions +// ============================================================================ + +pub fn remember(key: []const u8, value: []const u8, scope: u8) -> MemoryCell; + +pub fn recall(key: []const u8) -> RecallResult; + +pub fn recall_fuzzy(query: []const u8, top_k: usize) -> []RecallResult; + +pub fn forget(key: []const u8) -> ForgetOutcome; + +pub fn reflect() -> ReflectReport; + +pub fn recall_by_tag(tag: []const u8) -> []RecallResult; + +pub fn forget_scope(scope: u8) -> usize; + +// ============================================================================ +// Tests +// ============================================================================ + +test remember_recall_roundtrip { + const cell = remember("test_key", "test_value", SCOPE_SESSION); + assert(cell.key == "test_key"); + assert(cell.value == "test_value"); + assert(cell.scope == SCOPE_SESSION); + const result = recall("test_key"); + assert(result.found); + assert(result.cell.value == "test_value"); +} + +test forget_marks_tombstone { + const cell = remember("doomed", "data", SCOPE_AGENT); + const outcome = forget("doomed"); + assert(outcome.success); + assert(outcome.was_tombstoned); + const result = recall("doomed"); + assert(!result.found); +} + +test recall_missing_returns_not_found { + const result = recall("nonexistent_key_xyz"); + assert(!result.found); + assert(result.similarity == 0.0); +} + +test reflect_counts_by_scope { + forget_scope(SCOPE_AGENT); + forget_scope(SCOPE_SESSION); + forget_scope(SCOPE_PERMANENT); + _ = remember("a", "1", SCOPE_AGENT); + _ = remember("b", "2", SCOPE_SESSION); + _ = remember("c", "3", SCOPE_PERMANENT); + const report = reflect(); + assert(report.total_cells >= 3); + assert(report.by_scope[SCOPE_AGENT] >= 1); + assert(report.by_scope[SCOPE_SESSION] >= 1); + assert(report.by_scope[SCOPE_PERMANENT] >= 1); +} + +test remember_updates_existing { + _ = remember("up_key", "v1", SCOPE_SESSION); + const cell = remember("up_key", "v2", SCOPE_SESSION); + assert(cell.value == "v2"); + const result = recall("up_key"); + assert(result.cell.value == "v2"); +} + +test recall_by_tag { + const cell = remember("tagged", "data", SCOPE_SESSION); + // In a full implementation, tags would be set during remember + const results = recall_by_tag("session"); + assert(results.len >= 0); +} + +// ============================================================================ +// Invariants +// ============================================================================ + +invariant key_not_empty { + forall cell: MemoryCell :: cell.key.len > 0 +} + +invariant phi_hash_always_set { + forall cell: MemoryCell :: cell.phi_hash.len > 0 +} + +invariant scope_valid_range { + forall cell: MemoryCell :: cell.scope <= SCOPE_PERMANENT +} + +invariant created_before_updated { + forall cell: MemoryCell :: cell.created_ns <= cell.updated_ns +} + +invariant tombstone_not_recallable { + forall key: []const u8 :: forget(key).success implies !recall(key).found +} + +// ============================================================================ +// Benchmarks +// ============================================================================ + +bench remember_bench { + remember("bench_key", "bench_value", SCOPE_SESSION) +} expect < 1000 cycles + +bench recall_bench { + recall("bench_key") +} expect < 500 cycles + +bench forget_bench { + forget("bench_forget_key") +} expect < 500 cycles + +bench reflect_bench { + reflect() +} expect < 2000 cycles diff --git a/specs/memory/memory_scope.t27 b/specs/memory/memory_scope.t27 new file mode 100644 index 000000000..6271ec897 --- /dev/null +++ b/specs/memory/memory_scope.t27 @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: Apache-2.0 +// Module: MemoryScope — agent/session/permanent lifetime management +// Issue: #517 +// phi^2 + 1/phi^2 = 3 | TRINITY + +module MemoryScope; + +use memory::memory_primitives::SCOPE_AGENT; +use memory::memory_primitives::SCOPE_SESSION; +use memory::memory_primitives::SCOPE_PERMANENT; + +// ============================================================================ +// Constants +// ============================================================================ + +pub const VERSION : u32 = 1; +pub const MAX_SCOPES : usize = 3; +pub const AGENT_TTL_NS : u64 = 3600000000000; +pub const SESSION_TTL_NS : u64 = 86400000000000; +pub const PERMANENT_TTL_NS : u64 = 0; + +// ============================================================================ +// Types +// ============================================================================ + +pub const ScopePolicy = struct { + scope : u8, + ttl_ns : u64, + max_entries : usize, + max_bytes : usize, + evict_lru : bool, +}; + +pub const ScopeSnapshot = struct { + scope : u8, + entry_count : usize, + byte_count : usize, + oldest_ns : u64, + newest_ns : u64, +}; + +// ============================================================================ +// Functions +// ============================================================================ + +pub fn scope_init() -> bool; + +pub fn scope_get_policy(scope: u8) -> ?ScopePolicy; + +pub fn scope_set_policy(scope: u8, policy: ScopePolicy) -> bool; + +pub fn scope_snapshot(scope: u8) -> ?ScopeSnapshot; + +pub fn scope_evict(scope: u8, now_ns: u64) -> usize; + +pub fn scope_is_valid(scope: u8) -> bool; + +pub fn scope_ttl_for(scope: u8) -> u64; + +pub fn scope_close() -> bool; + +// ============================================================================ +// Tests +// ============================================================================ + +test scope_is_valid_all_three { + assert(scope_is_valid(SCOPE_AGENT)); + assert(scope_is_valid(SCOPE_SESSION)); + assert(scope_is_valid(SCOPE_PERMANENT)); + assert(!scope_is_valid(99)); +} + +test scope_ttl_defaults { + assert(scope_ttl_for(SCOPE_AGENT) == AGENT_TTL_NS); + assert(scope_ttl_for(SCOPE_SESSION) == SESSION_TTL_NS); + assert(scope_ttl_for(SCOPE_PERMANENT) == PERMANENT_TTL_NS); +} + +test scope_get_policy_returns_for_valid { + const p = scope_get_policy(SCOPE_AGENT); + assert(p != null); + assert(p.?.scope == SCOPE_AGENT); +} + +test scope_get_policy_null_for_invalid { + const p = scope_get_policy(200); + assert(p == null); +} + +test scope_set_policy_roundtrip { + scope_init(); + const policy = ScopePolicy{ + .scope = SCOPE_AGENT, + .ttl_ns = 999, + .max_entries = 50, + .max_bytes = 1024, + .evict_lru = true, + }; + const ok = scope_set_policy(SCOPE_AGENT, policy); + assert(ok); + const retrieved = scope_get_policy(SCOPE_AGENT); + assert(retrieved != null); + assert(retrieved.?.ttl_ns == 999); +} + +test scope_snapshot_returns_counts { + scope_init(); + const snap = scope_snapshot(SCOPE_AGENT); + assert(snap != null); + assert(snap.?.scope == SCOPE_AGENT); + assert(snap.?.entry_count >= 0); +} + +test scope_evict_returns_count { + scope_init(); + const evicted = scope_evict(SCOPE_AGENT, 0); + assert(evicted >= 0); +} + +// ============================================================================ +// Invariants +// ============================================================================ + +invariant only_three_scopes { + forall s: u8 :: scope_is_valid(s) implies (s <= SCOPE_PERMANENT) +} + +invariant ttl_ordering { + scope_ttl_for(SCOPE_AGENT) < scope_ttl_for(SCOPE_SESSION) +} + +invariant permanent_no_expiry { + scope_ttl_for(SCOPE_PERMANENT) == 0 +} + +invariant policy_scope_matches { + forall s: u8 :: + scope_get_policy(s) != null implies scope_get_policy(s).?.scope == s +} + +// ============================================================================ +// Benchmarks +// ============================================================================ + +bench scope_get_policy_bench { + scope_get_policy(SCOPE_AGENT) +} expect < 100 cycles + +bench scope_evict_bench { + scope_evict(SCOPE_AGENT, 0) +} expect < 5000 cycles + +bench scope_snapshot_bench { + scope_snapshot(SCOPE_SESSION) +} expect < 1000 cycles diff --git a/specs/memory/memory_store.t27 b/specs/memory/memory_store.t27 new file mode 100644 index 000000000..f3c138a49 --- /dev/null +++ b/specs/memory/memory_store.t27 @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: Apache-2.0 +// Module: MemoryStore — content-addressable backend for memory cells +// Issue: #517 +// phi^2 + 1/phi^2 = 3 | TRINITY + +module MemoryStore; + +use memory::memory_primitives::MemoryCell; +use memory::memory_primitives::SCOPE_AGENT; +use memory::memory_primitives::SCOPE_SESSION; +use memory::memory_primitives::SCOPE_PERMANENT; +use memory::memory_primitives::TOMBSTONE; + +// ============================================================================ +// Constants +// ============================================================================ + +pub const VERSION : u32 = 1; +pub const MAX_STORE_SIZE : usize = 1000000; +pub const HASH_ALGORITHM : str = "sha256"; +pub const BUCKET_COUNT : usize = 1024; + +// ============================================================================ +// Types +// ============================================================================ + +pub const StoreStats = struct { + total_entries : usize, + live_entries : usize, + tombstoned : usize, + total_bytes : usize, + bucket_collisions : usize, +}; + +pub const StoreEntry = struct { + hash : []const u8, + cell : MemoryCell, + is_tombstone : bool, + generation : u64, +}; + +// ============================================================================ +// Functions +// ============================================================================ + +pub fn store_init() -> bool; + +pub fn store_put(cell: MemoryCell) -> []const u8; + +pub fn store_get(key: []const u8) -> ?StoreEntry; + +pub fn store_delete(key: []const u8) -> bool; + +pub fn store_stats() -> StoreStats; + +pub fn store_compact() -> usize; + +pub fn store_hash(content: []const u8) -> []const u8; + +pub fn store_list_by_scope(scope: u8) -> []StoreEntry; + +pub fn store_close() -> bool; + +// ============================================================================ +// Tests +// ============================================================================ + +test store_put_get_roundtrip { + store_init(); + const cell = MemoryCell{ + .key = "s_test", + .value = "s_value", + .scope = SCOPE_AGENT, + .phi_hash = "abc123", + .created_ns = 1000, + .updated_ns = 1000, + .tags = &.{}, + }; + const hash = store_put(cell); + assert(hash.len > 0); + const entry = store_get("s_test"); + assert(entry != null); + assert(entry.?.cell.value == "s_value"); +} + +test store_delete_returns_true { + store_init(); + const cell = MemoryCell{ + .key = "s_del", + .value = "bye", + .scope = SCOPE_SESSION, + .phi_hash = "def456", + .created_ns = 2000, + .updated_ns = 2000, + .tags = &.{}, + }; + _ = store_put(cell); + const ok = store_delete("s_del"); + assert(ok); + const entry = store_get("s_del"); + assert(entry == null); +} + +test store_stats_reflect_state { + store_init(); + const stats = store_stats(); + assert(stats.total_entries >= 0); + assert(stats.live_entries >= 0); + assert(stats.tombstoned >= 0); +} + +test store_hash_deterministic { + const h1 = store_hash("hello"); + const h2 = store_hash("hello"); + assert(h1 == h2); +} + +test store_compact_removes_tombstones { + store_init(); + const cell = MemoryCell{ + .key = "c_me", + .value = "gone", + .scope = SCOPE_AGENT, + .phi_hash = "ghi789", + .created_ns = 3000, + .updated_ns = 3000, + .tags = &.{}, + }; + _ = store_put(cell); + _ = store_delete("c_me"); + const removed = store_compact(); + assert(removed >= 1); +} + +test store_list_by_scope_filters { + store_init(); + const a = MemoryCell{ + .key = "l_a", + .value = "a", + .scope = SCOPE_AGENT, + .phi_hash = "h1", + .created_ns = 4000, + .updated_ns = 4000, + .tags = &.{}, + }; + const s = MemoryCell{ + .key = "l_s", + .value = "s", + .scope = SCOPE_SESSION, + .phi_hash = "h2", + .created_ns = 4001, + .updated_ns = 4001, + .tags = &.{}, + }; + _ = store_put(a); + _ = store_put(s); + const agent_list = store_list_by_scope(SCOPE_AGENT); + assert(agent_list.len >= 1); +} + +// ============================================================================ +// Invariants +// ============================================================================ + +invariant hash_deterministic { + forall content: []const u8 :: store_hash(content) == store_hash(content) +} + +invariant unique_keys { + forall a: StoreEntry, b: StoreEntry :: + a.cell.key == b.cell.key implies a.hash == b.hash +} + +invariant stats_consistent { + let s = store_stats(); + s.live_entries + s.tombstoned == s.total_entries +} + +// ============================================================================ +// Benchmarks +// ============================================================================ + +bench store_put_bench { + const cell = MemoryCell{ + .key = "b_key", + .value = "b_val", + .scope = SCOPE_AGENT, + .phi_hash = "bench_hash", + .created_ns = 0, + .updated_ns = 0, + .tags = &.{}, + }; + store_put(cell) +} expect < 2000 cycles + +bench store_get_bench { + store_get("b_key") +} expect < 1000 cycles