feat(card): Card::new(label) — alternativa segura a Default::default()

Cierra la trap documentada de Card::default() que devuelve id =
Ulid::nil(). Usar Card::default() viva colisionaba con cualquier otra
Card default-construida bajo el mismo id 00000...

La fix no es romper Default (sigue siendo determinista, requerido por
callers que lo usan como template para deserializacion y patterns de
busqueda) sino agregar un constructor explicito Card::new(label) que
asigna id = Ulid::new() + label provisto, manteniendo defaults seguros
en todo lo demas.

Pensado para struct-literals con override parcial:

    let card = Card {
        kind: CardKind::Data,
        payload: Payload::Embedded(json),
        ..Card::new("mi-modulo.algo")
    };

Refactor de call sites en codigo de produccion:
- brahman_sidecar::discovery::build_consumer_card
- nouser daemon::build_engine_card

Default queda con docstring expandida que apunta a Card::new para uso
"vivo". to_brahman_card en nouser-card NO se modifica porque asigna
el id estable de la Monada, no uno fresco.

Tests: 3 unitarios nuevos en brahman-card. 15 tests verdes (era 12).
This commit is contained in:
Sergio
2026-05-09 02:43:21 +00:00
parent 006640057a
commit 4c9e4c3962
4 changed files with 97 additions and 13 deletions
+34
View File
@@ -6,6 +6,40 @@ ratio/diff ver `git show <sha>`.
## 2026-05-09
### feat(card): `Card::new(label)` — alternativa segura a `Default::default()`
Cierra la trap documentada de `Card::default()` que devuelve `id =
Ulid::nil()`. Usar `Card::default()` "viva" colisionaba con cualquier
otra Card default-construida bajo el mismo id `00000000…`. La fix no
es romper `Default` (sigue siendo determinista, requerido por callers
que lo usan como template para deserialización), sino agregar un
constructor explícito:
let card = Card {
kind: CardKind::Data,
payload: Payload::Embedded(json),
..Card::new("mi-modulo.algo")
};
`Card::new(label)` asigna `id = Ulid::new()` (único) + `label`
provisto, dejando el resto en defaults seguros (Virtual / OneShot /
Ente). Pensado para usarse en struct-literals con override parcial,
igual sintaxis que el patrón viejo pero sin la trap.
Refactor de call sites:
- `brahman_sidecar::discovery::build_consumer_card``..Card::new(label)`
- `nouser daemon::build_engine_card``..Card::new("brahman.nouser_engine")`
`Default` se mantiene tal cual con docstring expandida que advierte
explícitamente sobre el uso "vivo" y apunta a `Card::new`. Tests
existentes y el patrón `nouser_card::MonadManifest::to_brahman_card`
(que asigna el id estable de la Mónada, no uno fresco) NO se
modifican — `Default` sigue siendo correcto cuando el caller
sobreescribe `id` explícitamente.
Tests: 3 unitarios nuevos en brahman-card (`new_assigns_real_ulid_and_label`,
`new_yields_distinct_ids_per_call`, `default_keeps_nil_id_for_struct_update_pattern`).
15 tests verdes (era 12).
### feat(sidecar): API reusable de discovery vía broker
Promueve el patrón ad-hoc `discover_producer_socket` (que vivía
inline en `nouser attract --remote`) a un módulo público