chore: monorepo inicial con arje + minga + yahweh absorbidos

Workspace en 4 ejes (core/modules/apps/shared):

- core/: 24 crates de arje (Init systemd-compatible: ente-card, ente-zero,
  ente-kernel, ente-bus, ente-cas, ente-soma, ente-wasm, ente-snapshot,
  ente-brain, ente-echo, ente-policy-provider, + 12 crates *-compat)
- modules/semantic_dht/: 5 crates de minga (minga-core con AST/CAS/MST,
  minga-p2p con libp2p Kad, minga-store, minga-vfs, minga-cli)
- modules/ui_engine/: 11 crates de yahweh (libs/{core,theme,bus,providers},
  widgets/{tree,splitter,tabs,tiled,container_core,text_input})
- apps/: 5 crates de yahweh (file_explorer, database_explorer, text_viewer,
  image_viewer, yahweh-shell)
- shared_wit/protocol.wit: handshake/lifecycle inicial

Cargo.toml unificado: thiserror bumped a 2 (transparente para arje), tokio
"full", paths intra-workspace de yahweh redirigidos a su nueva ubicación.

cargo check --workspace: 0 errores, 17 warnings (dead code preexistente).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-08 04:45:44 +00:00
commit 53dbdf0f1d
176 changed files with 34845 additions and 0 deletions
@@ -0,0 +1,113 @@
//! Invariantes del `SledAttestationStore`.
use minga_core::{Attestation, AttestationError, ContentHash, Keypair};
use minga_store::{SledAttestationStore, StoreError};
use tempfile::TempDir;
fn open_store(path: &std::path::Path) -> (sled::Db, SledAttestationStore) {
let db = sled::open(path).unwrap();
let store = SledAttestationStore::open_tree(&db, "atts").unwrap();
(db, store)
}
fn kp(seed: u8) -> Keypair {
Keypair::from_seed(&[seed; 32])
}
fn ch(seed: u8) -> ContentHash {
ContentHash([seed; 32])
}
#[test]
fn add_then_get_roundtrips() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let alice = kp(1);
let att = Attestation::create(&alice, ch(7));
store.add(att.clone()).unwrap();
let retrieved = store.get(&ch(7)).unwrap();
assert_eq!(retrieved, vec![att]);
}
#[test]
fn invalid_signature_rejected() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let alice = kp(1);
let mut att = Attestation::create(&alice, ch(7));
att.signature.0[10] ^= 0xFF;
let r = store.add(att);
assert!(matches!(
r,
Err(StoreError::Attestation(AttestationError::InvalidSignature))
));
assert_eq!(store.len(), 0);
}
#[test]
fn idempotent_per_author_and_content() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let alice = kp(1);
let att = Attestation::create(&alice, ch(5));
store.add(att.clone()).unwrap();
store.add(att.clone()).unwrap();
store.add(att).unwrap();
assert_eq!(store.len(), 1);
}
#[test]
fn multiple_authors_per_content() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let alice = kp(1);
let bob = kp(2);
let carol = kp(3);
let h = ch(99);
store.add(Attestation::create(&alice, h)).unwrap();
store.add(Attestation::create(&bob, h)).unwrap();
store.add(Attestation::create(&carol, h)).unwrap();
assert_eq!(store.len(), 3);
let authors = store.authors_of(&h).unwrap();
assert_eq!(authors.len(), 3);
assert!(authors.contains(&alice.did()));
assert!(authors.contains(&bob.did()));
assert!(authors.contains(&carol.did()));
}
#[test]
fn data_persists_across_reopen() {
let dir = TempDir::new().unwrap();
let path = dir.path();
let alice = kp(42);
let h = ch(11);
{
let (db, store) = open_store(path);
store.add(Attestation::create(&alice, h)).unwrap();
store.flush().unwrap();
drop(store);
drop(db);
}
{
let (_db, store) = open_store(path);
let authors = store.authors_of(&h).unwrap();
assert_eq!(authors, vec![alice.did()]);
}
}
#[test]
fn unknown_content_returns_empty() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let authors = store.authors_of(&ch(0)).unwrap();
assert!(authors.is_empty());
}
@@ -0,0 +1,67 @@
//! Tests de persistencia del keypair cifrado en disco.
use minga_core::Keypair;
use minga_store::keypair_file;
use tempfile::TempDir;
#[test]
fn save_then_load_preserves_identity() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("keypair");
let original = Keypair::from_seed(&[7; 32]);
keypair_file::save(&original, &path, "secreto42").unwrap();
let loaded = keypair_file::load(&path, "secreto42").unwrap();
assert_eq!(loaded.did(), original.did());
let msg = b"el peer sigue siendo el mismo";
let sig = loaded.sign(msg);
assert!(original.did().verify(msg, &sig));
}
#[test]
fn load_with_wrong_passphrase_errors() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("keypair");
let kp = Keypair::from_seed(&[3; 32]);
keypair_file::save(&kp, &path, "correcta").unwrap();
let r = keypair_file::load(&path, "incorrecta");
assert!(r.is_err());
}
#[test]
fn load_missing_file_errors() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("no-existe");
let r = keypair_file::load(&path, "x");
assert!(r.is_err());
}
#[test]
fn save_overwrites_existing() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("keypair");
let first = Keypair::from_seed(&[1; 32]);
keypair_file::save(&first, &path, "pass").unwrap();
let second = Keypair::from_seed(&[2; 32]);
keypair_file::save(&second, &path, "pass").unwrap();
let loaded = keypair_file::load(&path, "pass").unwrap();
assert_eq!(loaded.did(), second.did());
assert_ne!(loaded.did(), first.did());
}
#[test]
fn file_size_is_compact() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("keypair");
keypair_file::save(&Keypair::from_seed(&[5; 32]), &path, "p").unwrap();
let size = std::fs::metadata(&path).unwrap().len();
// 8 magic + 1 version + 16 salt + 12 nonce + 32 secret + 16 tag = 85.
assert_eq!(size, 85);
}
@@ -0,0 +1,119 @@
//! Invariantes del `SledMstStore`. La propiedad clave: el `Mst`
//! reconstruido desde disco produce el mismo `root_hash` que el `Mst`
//! que insertamos — la estructura es derivable solo de las claves.
use minga_core::{ContentHash, Mst};
use minga_store::SledMstStore;
use tempfile::TempDir;
fn open_store(path: &std::path::Path) -> (sled::Db, SledMstStore) {
let db = sled::open(path).unwrap();
let store = SledMstStore::open_tree(&db, "mst").unwrap();
(db, store)
}
fn ch(seed: u64) -> ContentHash {
let h = blake3::hash(&seed.to_le_bytes());
ContentHash(*h.as_bytes())
}
#[test]
fn insert_and_contains() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let h = ch(1);
assert!(!store.contains(&h).unwrap());
assert!(store.insert(h).unwrap());
assert!(store.contains(&h).unwrap());
// Idempotencia: re-insertar devuelve false.
assert!(!store.insert(h).unwrap());
assert_eq!(store.len(), 1);
}
#[test]
fn iter_returns_sorted_keys() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let hashes: Vec<ContentHash> = (0..32u64).map(ch).collect();
for h in &hashes {
store.insert(*h).unwrap();
}
let collected: Vec<ContentHash> = store.iter().map(|r| r.unwrap()).collect();
let mut sorted = hashes.clone();
sorted.sort();
assert_eq!(collected, sorted);
}
#[test]
fn root_hash_matches_in_memory_mst() {
// La propiedad fundacional: persistir solo las claves y reconstruir
// el árbol da exactamente el mismo `root_hash` que un `Mst`
// construido en memoria con las mismas claves.
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let mut in_memory = Mst::new();
for i in 0..50u64 {
let h = ch(i);
store.insert(h).unwrap();
in_memory.insert(h);
}
let reconstructed = store.to_in_memory().unwrap();
assert_eq!(reconstructed.root_hash(), in_memory.root_hash());
assert_eq!(reconstructed.len(), in_memory.len());
}
#[test]
fn data_persists_across_reopen() {
let dir = TempDir::new().unwrap();
let path = dir.path();
let hashes: Vec<ContentHash> = (0..20u64).map(ch).collect();
let target_root_hash;
{
let (db, store) = open_store(path);
for h in &hashes {
store.insert(*h).unwrap();
}
target_root_hash = store.to_in_memory().unwrap().root_hash();
store.flush().unwrap();
drop(store);
drop(db);
}
{
let (_db, store) = open_store(path);
let reconstructed = store.to_in_memory().unwrap();
assert_eq!(reconstructed.root_hash(), target_root_hash);
assert_eq!(reconstructed.len(), 20);
}
}
#[test]
fn order_independent_persistence() {
// Insertar las mismas claves en orden distinto produce el mismo
// `root_hash`. Equivalencia con la garantía del MST in-memory.
let dir1 = TempDir::new().unwrap();
let dir2 = TempDir::new().unwrap();
let hashes: Vec<ContentHash> = (0..30u64).map(ch).collect();
let (_db1, s1) = open_store(dir1.path());
for h in &hashes {
s1.insert(*h).unwrap();
}
let (_db2, s2) = open_store(dir2.path());
for h in hashes.iter().rev() {
s2.insert(*h).unwrap();
}
assert_eq!(
s1.to_in_memory().unwrap().root_hash(),
s2.to_in_memory().unwrap().root_hash()
);
}
@@ -0,0 +1,145 @@
//! Invariantes del `SledNodeStore`. Cubre:
//! - Round-trip estructural (lo que entra sale igual).
//! - Hash consistente con `cas::hash_node`.
//! - Idempotencia.
//! - Persistencia tras cerrar y reabrir el DB.
//! - Rechazo de `put_chunked` con hash inconsistente.
use minga_core::{cas::hash_components, hash_node, parse, ContentHash, StoredNode};
use minga_store::{SledNodeStore, StoreError};
use tempfile::TempDir;
fn open_store(path: &std::path::Path) -> (sled::Db, SledNodeStore) {
let db = sled::open(path).unwrap();
let store = SledNodeStore::open_tree(&db, "nodes").unwrap();
(db, store)
}
#[test]
fn round_trip_preserves_tree() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let original = parse::rust("fn add(x: i32, y: i32) -> i32 { x + y }").unwrap();
let h = store.put(&original).unwrap();
let reconstructed = store.reconstruct(&h).unwrap().unwrap();
assert_eq!(original, reconstructed);
}
#[test]
fn put_hash_matches_cas() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let n = parse::rust("fn f() -> bool { true }").unwrap();
let h_via_put = store.put(&n).unwrap();
let h_via_cas = hash_node(&n);
assert_eq!(h_via_put, h_via_cas);
}
#[test]
fn put_is_idempotent() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let n = parse::rust("fn f() { 1 + 2 + 3 }").unwrap();
let h1 = store.put(&n).unwrap();
let len_after_first = store.len();
let h2 = store.put(&n).unwrap();
let len_after_second = store.len();
assert_eq!(h1, h2);
assert_eq!(len_after_first, len_after_second);
}
#[test]
fn data_persists_across_reopen() {
let dir = TempDir::new().unwrap();
let path = dir.path();
let original = parse::rust("fn squared(n: i32) -> i32 { n * n }").unwrap();
let h;
{
let (db, store) = open_store(path);
h = store.put(&original).unwrap();
store.flush().unwrap();
drop(store);
drop(db);
}
{
let (_db, store) = open_store(path);
let reconstructed = store.reconstruct(&h).unwrap().unwrap();
assert_eq!(reconstructed, original);
}
}
#[test]
fn shared_subtrees_dedup() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let a = parse::rust("fn alpha() -> i32 { 1 + 2 }").unwrap();
let b = parse::rust("fn beta() -> i32 { 1 + 2 }").unwrap();
store.put(&a).unwrap();
let count_after_a = store.len();
store.put(&b).unwrap();
let count_after_b = store.len();
// El cuerpo `{ 1 + 2 }` y subnodos son idénticos: comparten
// entrada en sled. Crecimiento estricto pero menor que duplicar.
assert!(
count_after_b < 2 * count_after_a,
"dedup falló: {} >= 2 * {}",
count_after_b,
count_after_a
);
}
#[test]
fn put_chunked_rejects_hash_mismatch() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let stored = StoredNode {
kind: "function_item".to_string(),
field_name: None,
leaf_text: None,
children: Vec::new(),
};
let bogus_hash = ContentHash([0xAB; 32]);
let result = store.put_chunked(bogus_hash, &stored);
assert!(matches!(result, Err(StoreError::HashMismatch)));
assert!(!store.contains(&bogus_hash).unwrap());
}
#[test]
fn put_chunked_accepts_correct_hash() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let stored = StoredNode {
kind: "integer_literal".to_string(),
field_name: None,
leaf_text: Some(b"42".to_vec()),
children: Vec::new(),
};
let real_hash = hash_components(
&stored.kind,
stored.field_name.as_deref(),
stored.leaf_text.as_deref(),
&stored.children,
);
store.put_chunked(real_hash, &stored).unwrap();
assert!(store.contains(&real_hash).unwrap());
let retrieved = store.get(&real_hash).unwrap().unwrap();
assert_eq!(retrieved, stored);
}
#[test]
fn unknown_hash_returns_none() {
let dir = TempDir::new().unwrap();
let (_db, store) = open_store(dir.path());
let bogus = ContentHash([0xFE; 32]);
assert_eq!(store.get(&bogus).unwrap(), None);
assert_eq!(store.reconstruct(&bogus).unwrap(), None);
assert!(!store.contains(&bogus).unwrap());
}
@@ -0,0 +1,104 @@
//! Test de integración del `PersistentRepo`: los tres stores conviven
//! en una misma `sled::Db`, escritos en una sesión y recuperados
//! intactos en la siguiente.
use minga_core::{parse, Attestation, ContentHash, Keypair};
use minga_store::PersistentRepo;
use tempfile::TempDir;
#[test]
fn three_stores_persist_together_across_reopen() {
let dir = TempDir::new().unwrap();
let path = dir.path();
let alice = Keypair::from_seed(&[1; 32]);
// ── Sesión 1: poblamos el repo ──────────────────────────────────
let function_hash;
let target_root_hash;
{
let repo = PersistentRepo::open(path).unwrap();
let n = parse::rust("fn add(x: i32, y: i32) -> i32 { x + y }").unwrap();
function_hash = repo.nodes.put(&n).unwrap();
repo.mst.insert(function_hash).unwrap();
repo.attestations
.add(Attestation::create(&alice, function_hash))
.unwrap();
target_root_hash = repo.mst.to_in_memory().unwrap().root_hash();
repo.flush().unwrap();
}
// ── Sesión 2: reabrimos y verificamos integridad ────────────────
{
let repo = PersistentRepo::open(path).unwrap();
// Nodo recuperable.
let stored = repo.nodes.get(&function_hash).unwrap().unwrap();
assert_eq!(stored.kind, "source_file");
// Reconstrucción completa idéntica al original.
let reconstructed = repo.nodes.reconstruct(&function_hash).unwrap().unwrap();
let original = parse::rust("fn add(x: i32, y: i32) -> i32 { x + y }").unwrap();
assert_eq!(reconstructed, original);
// MST: misma raíz tras reconstruir.
assert_eq!(
repo.mst.to_in_memory().unwrap().root_hash(),
target_root_hash
);
// Atestación: sigue ahí, sigue verificable.
let authors = repo.attestations.authors_of(&function_hash).unwrap();
assert_eq!(authors, vec![alice.did()]);
let atts = repo.attestations.get(&function_hash).unwrap();
assert!(atts[0].verify());
}
}
#[test]
fn repo_supports_multiple_functions_and_authors() {
let dir = TempDir::new().unwrap();
let repo = PersistentRepo::open(dir.path()).unwrap();
let alice = Keypair::from_seed(&[1; 32]);
let bob = Keypair::from_seed(&[2; 32]);
let mut hashes: Vec<ContentHash> = Vec::new();
for src in &[
"fn one() -> i32 { 1 }",
"fn two() -> i32 { 2 }",
"fn three(x: i32) -> i32 { x + 1 }",
] {
let n = parse::rust(src).unwrap();
let h = repo.nodes.put(&n).unwrap();
repo.mst.insert(h).unwrap();
hashes.push(h);
}
// Alice firma las tres; Bob firma solo la primera.
for h in &hashes {
repo.attestations
.add(Attestation::create(&alice, *h))
.unwrap();
}
repo.attestations
.add(Attestation::create(&bob, hashes[0]))
.unwrap();
repo.flush().unwrap();
assert_eq!(repo.mst.len(), 3);
assert_eq!(repo.attestations.len(), 4);
// La función firmada por ambos tiene dos autores.
let authors_first = repo.attestations.authors_of(&hashes[0]).unwrap();
assert_eq!(authors_first.len(), 2);
assert!(authors_first.contains(&alice.did()));
assert!(authors_first.contains(&bob.did()));
// Las otras dos solo tienen a Alice.
assert_eq!(
repo.attestations.authors_of(&hashes[1]).unwrap(),
vec![alice.did()]
);
}