feat(broker): priority contexts — biases per-contexto operativo
Cierra el último pendiente de feature: el broker ahora puede operar
bajo un contexto (test/prod/foreground/secure/etc) que activa biases
declarados en las Cards.
Schema (brahman-card):
- ContextBias { pin_to: Option<String>, priority_offset: i8 }.
- Card.priority_contexts: BTreeMap<String, ContextBias>, también en
WireCard. Las conversiones From propagan el campo.
Comportamiento (brahman-broker):
- BrokerConfig.current_context: Option<String>. Cuando es Some(ctx) y
una Card tiene priority_contexts.get(ctx), el bias aplica:
- Consumer-side: bias.pin_to sobreescribe Flow.pin_to estático.
- Producer-side: bias.priority_offset se suma a la priority base
(clamp en [Low=0, Critical=3]).
- BrokeredCard propaga priority_contexts. find_producer_for usa
effective_priority y context_bias en lugar de comparar Priority
directo.
Observabilidad:
- AdminConfig.current_context + StatusSnapshot.current_context.
- brahman-status imprime "Context: <nombre>" si está activo.
Wiring:
- ente-zero lee BRAHMAN_BROKER_CONTEXT del entorno y la propaga al
broker y al admin. Sin var, biases inactivos (back-compat total).
Tests nuevos (brahman-broker, +4):
- context_priority_offset_lifts_producer_above_alphabetic_winner:
sin contexto a-prod gana por alfabético; con context "test" b-prod
gana por offset +1.
- context_pin_to_overrides_static_pin: static pin "real-dht", test
override "mock-dht" → mock gana en context "test".
- unknown_context_no_op: biases declarados para "test" no aplican
cuando broker está en "prod".
- priority_offset_clamps_to_critical: offset enorme se clampa a 3.
Validación end-to-end manual:
$ BRAHMAN_BROKER_CONTEXT=test ente-zero &
$ brahman-status
Init: server=0.1.0 protocol=0.1.0 attached=true
Context: test
Tests acumulados: 39 (card 11, broker 15, handshake codec+transport 2 +
integ 7, card-wit 4, admin 0). cargo check --workspace: 0 errores, 0
warnings.
Con esto cierran TODOS los pendientes técnicos abiertos. El único
"pendiente" que queda es el caso real para extender (priority
contexts per-deployment, scheduling biases dinámicos, etc.).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -130,6 +130,13 @@ pub struct Card {
|
||||
#[serde(default)]
|
||||
pub genesis: Vec<Card>,
|
||||
|
||||
/// Biases per-contexto. La key es el nombre del contexto (p. ej.
|
||||
/// `"test"`, `"prod"`, `"foreground"`). Cuando el broker está
|
||||
/// configurado bajo ese contexto, el bias se aplica. Sin contexto
|
||||
/// activo o sin entrada matching, este campo no afecta el ranking.
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub priority_contexts: BTreeMap<String, ContextBias>,
|
||||
|
||||
/// Campos JSON/TOML desconocidos preservados durante I/O de archivos
|
||||
/// (forward-compat). **No se transmiten por wire (postcard)** — la
|
||||
/// proyección a [`WireCard`] los descarta porque `serde_json::Value`
|
||||
@@ -159,6 +166,7 @@ impl Default for Card {
|
||||
priority: Priority::default(),
|
||||
flow: Flows::default(),
|
||||
genesis: Vec::new(),
|
||||
priority_contexts: BTreeMap::new(),
|
||||
extensions: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
@@ -386,6 +394,36 @@ pub enum Priority {
|
||||
Critical,
|
||||
}
|
||||
|
||||
/// Override per-contexto sobre los matches del broker.
|
||||
///
|
||||
/// La Card declara biases bajo `priority_contexts.<nombre>` que se
|
||||
/// activan cuando el broker corre bajo ese contexto. Aplicación según rol:
|
||||
///
|
||||
/// - **Como consumidor**: `pin_to` sobreescribe el `pin_to` estático del
|
||||
/// `Flow.pin_to` durante la búsqueda de productores.
|
||||
/// - **Como productor**: `priority_offset` se suma a la priority base
|
||||
/// (saturando en `[Low, Critical]`) para el ranking de candidatos.
|
||||
///
|
||||
/// Casos de uso típicos: test↔prod (mock vs real), foreground↔background
|
||||
/// (latencia vs costo), trust gates (sólo productores con cierto nivel).
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ContextBias {
|
||||
/// Override del `pin_to` estático cuando el broker está en este
|
||||
/// contexto y la Card actúa como consumidor.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub pin_to: Option<String>,
|
||||
|
||||
/// Modifica la priority efectiva del Card como productor.
|
||||
/// `+1` lo eleva, `-1` lo baja. El resultado se clampa al rango de
|
||||
/// `Priority` ([Low, Critical]).
|
||||
#[serde(default, skip_serializing_if = "is_zero_i8")]
|
||||
pub priority_offset: i8,
|
||||
}
|
||||
|
||||
fn is_zero_i8(v: &i8) -> bool {
|
||||
*v == 0
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Flujos tipados (del modelo brahman)
|
||||
// =====================================================================
|
||||
@@ -702,6 +740,8 @@ pub struct WireCard {
|
||||
pub flow: Flows,
|
||||
#[serde(default)]
|
||||
pub genesis: Vec<WireCard>,
|
||||
#[serde(default)]
|
||||
pub priority_contexts: BTreeMap<String, ContextBias>,
|
||||
}
|
||||
|
||||
impl From<Card> for WireCard {
|
||||
@@ -721,6 +761,7 @@ impl From<Card> for WireCard {
|
||||
priority: c.priority,
|
||||
flow: c.flow,
|
||||
genesis: c.genesis.into_iter().map(WireCard::from).collect(),
|
||||
priority_contexts: c.priority_contexts,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -742,6 +783,7 @@ impl From<WireCard> for Card {
|
||||
priority: w.priority,
|
||||
flow: w.flow,
|
||||
genesis: w.genesis.into_iter().map(Card::from).collect(),
|
||||
priority_contexts: w.priority_contexts,
|
||||
extensions: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user