feat: Crossreferencia — Card.references como grafo del fractal

Las Cards ahora declaran sus relaciones con otras Cards. El Engine
posee Mónadas; las Mónadas declaran que son poseídas por el Engine.

- brahman-card:
  - RelationshipKind { Owns, OwnedBy, Processes, ProcessedBy, Sibling }
  - CardReference { kind, target_id: Ulid, target_label: String }.
    target_label es cache para que la UI renderee sin resolver.
  - Card.references: Vec<CardReference> + espejo en WireCard.
    Conversiones From propagan.
- brahman-broker::BrokeredCard propaga references.
- brahman-status imprime "ref OwnedBy → label (id)" por sesión.
- nouser daemon: cada Mónada publicada añade OwnedBy apuntando al
  engine. Declaración unilateral — el engine no necesita conocer
  Mónada IDs de antemano.

Validación end-to-end:
  $ ente-zero & nouser daemon crates/core
  $ brahman-status
  Sessions (6):
    [ente]  brahman.nouser_engine
    [data]  brahman-handshake/src
        ref OwnedBy  →  brahman.nouser_engine  (01K...)
    [data]  ente-brain/src
        ref OwnedBy  →  brahman.nouser_engine  (01K...)
    ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-08 19:44:47 +00:00
parent 5edc912ed8
commit b3feaf667c
5 changed files with 103 additions and 5 deletions
+14 -4
View File
@@ -166,9 +166,11 @@ fn cmd_daemon(args: &[String]) -> Cmd {
// 1. El propio engine se presenta como Ente.
let engine_card = build_engine_card();
let engine_id = engine_card.id;
let engine_label = engine_card.label.clone();
eprintln!(
"nouser daemon: publicando engine '{}' (kind=Ente)",
engine_card.label
"nouser daemon: publicando engine '{}' (kind=Ente, id={})",
engine_label, engine_id
);
brahman_sidecar::spawn(engine_card);
@@ -181,10 +183,18 @@ fn cmd_daemon(args: &[String]) -> Cmd {
db.monad_count()
);
// 3. Cada Mónada se presenta como Card de tipo Data.
// 3. Cada Mónada se presenta como Card de tipo Data, declarando
// su relación OwnedBy con el engine. La UI puede entonces
// cruzar referencias para reconstruir el grafo
// "nouser_engine posee Mónada X" sin lookup adicional.
let mut handles = Vec::with_capacity(db.monad_count());
for monad in db.monads() {
let card = monad.to_brahman_card();
let mut card = monad.to_brahman_card();
card.references.push(brahman_card::CardReference {
kind: brahman_card::RelationshipKind::OwnedBy,
target_id: engine_id,
target_label: engine_label.clone(),
});
match brahman_sidecar::spawn_with_handle(brahman_sidecar::SidecarConfig::new(card)) {
Ok(h) => handles.push(h),
Err(e) => eprintln!(