Trigger-evaluering i portvokteren (oppgave 24.2)
Ved node/edge-events fra PG LISTEN/NOTIFY evaluerer portvokteren nå om noen orchestration-noder matcher triggeren. Implementert som non-blocking async task som ikke blokkerer WebSocket-flyten. Ny modul orchestration_trigger.rs: - Mapper NOTIFY-events til trigger-typer (node.created, edge.created) - Effektiv lookup via funksjonell B-tree-indeks på metadata->trigger->event - Evaluerer observes-edges (eksplisitt) vs conditions (implisitt) - Betingelser: node_kind, edge_type, has_trait, has_tag (AND-logikk) - Legger matchende orkestreringer i jobbkøen som "orchestrate"-jobb Ny migration 021: indeks for trigger-event lookup på orchestration-noder. Jobbkø-dispatcher håndterer "orchestrate" med placeholder (24.3 implementerer utførelse). Verifisert: content-node trigrer matching orchestration, communication-node hoppes over.
This commit is contained in:
parent
b759808831
commit
26f03ef21d
6 changed files with 389 additions and 2 deletions
|
|
@ -217,6 +217,23 @@ async fn dispatch(
|
||||||
"pg_delete_edge" => {
|
"pg_delete_edge" => {
|
||||||
pg_writes::handle_delete_edge(job, db, index_cache).await
|
pg_writes::handle_delete_edge(job, db, index_cache).await
|
||||||
}
|
}
|
||||||
|
// Orchestration: trigger-evaluering har lagt jobben i kø,
|
||||||
|
// men utførelsen implementeres i oppgave 24.3.
|
||||||
|
// Foreløpig logger vi og returnerer OK.
|
||||||
|
"orchestrate" => {
|
||||||
|
let orch_id = job.payload.get("orchestration_id")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("ukjent");
|
||||||
|
tracing::info!(
|
||||||
|
orchestration_id = %orch_id,
|
||||||
|
"Orchestrate-jobb mottatt (utførelse kommer i oppgave 24.3)"
|
||||||
|
);
|
||||||
|
Ok(serde_json::json!({
|
||||||
|
"status": "pending_implementation",
|
||||||
|
"orchestration_id": orch_id,
|
||||||
|
"message": "Orchestration execution not yet implemented (task 24.3)"
|
||||||
|
}))
|
||||||
|
}
|
||||||
other => Err(format!("Ukjent jobbtype: {other}")),
|
other => Err(format!("Ukjent jobbtype: {other}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ mod serving;
|
||||||
pub mod summarize;
|
pub mod summarize;
|
||||||
pub mod ws;
|
pub mod ws;
|
||||||
pub mod mixer;
|
pub mod mixer;
|
||||||
|
pub mod orchestration_trigger;
|
||||||
pub mod tiptap;
|
pub mod tiptap;
|
||||||
pub mod transcribe;
|
pub mod transcribe;
|
||||||
pub mod tts;
|
pub mod tts;
|
||||||
|
|
|
||||||
312
maskinrommet/src/orchestration_trigger.rs
Normal file
312
maskinrommet/src/orchestration_trigger.rs
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
//! Trigger-evaluering for orchestration-noder.
|
||||||
|
//!
|
||||||
|
//! Ved node/edge-events fra PG LISTEN/NOTIFY evaluerer vi om noen
|
||||||
|
//! orchestration-noder matcher triggeren. Matchende orkestreringer
|
||||||
|
//! legges i jobbkøen for utførelse (oppgave 24.3).
|
||||||
|
//!
|
||||||
|
//! Designprinsipper:
|
||||||
|
//! - Effektiv lookup via indeks på `metadata->'trigger'->>'event'`
|
||||||
|
//! - Ingen LLM-kall — ren deterministisk evaluering
|
||||||
|
//! - Non-blocking — spawner async task, blokkerer ikke NOTIFY-loopen
|
||||||
|
//! - `observes`-edge overtrumfer conditions (eksplisitt > implisitt)
|
||||||
|
//!
|
||||||
|
//! Ref: docs/concepts/orkestrering.md § 5–6
|
||||||
|
|
||||||
|
use sqlx::PgPool;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// Kontekst for en trigger-event — informasjonen som orkestreringen
|
||||||
|
/// trenger for å evaluere betingelser og bygge payload.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TriggerContext {
|
||||||
|
/// Trigger-event-type (f.eks. "node.created", "edge.created")
|
||||||
|
pub event: String,
|
||||||
|
/// Primær node-ID som utløste eventet (node selv, eller source/target)
|
||||||
|
pub node_id: Option<Uuid>,
|
||||||
|
/// Node-kind (f.eks. "content", "communication") — for node_kind-betingelse
|
||||||
|
pub node_kind: Option<String>,
|
||||||
|
/// Edge-type (for edge-events)
|
||||||
|
pub edge_type: Option<String>,
|
||||||
|
/// Source-ID (for edge-events)
|
||||||
|
pub source_id: Option<Uuid>,
|
||||||
|
/// Target-ID (for edge-events)
|
||||||
|
pub target_id: Option<Uuid>,
|
||||||
|
/// Operasjon (INSERT, UPDATE, DELETE)
|
||||||
|
pub op: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matchende orchestration-node med metadata for jobb-oppretting.
|
||||||
|
#[derive(Debug, sqlx::FromRow)]
|
||||||
|
struct OrchestrationMatch {
|
||||||
|
id: Uuid,
|
||||||
|
metadata: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawner trigger-evaluering som en async task.
|
||||||
|
/// Blokkerer ikke NOTIFY-loopen — feil logges men propageres ikke.
|
||||||
|
pub fn spawn_trigger_evaluation(db: PgPool, ctx: TriggerContext) {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
if let Err(e) = evaluate_triggers(&db, &ctx).await {
|
||||||
|
tracing::error!(
|
||||||
|
event = %ctx.event,
|
||||||
|
error = %e,
|
||||||
|
"Trigger-evaluering feilet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluerer alle orchestration-noder som matcher et gitt trigger-event.
|
||||||
|
///
|
||||||
|
/// Flyt:
|
||||||
|
/// 1. Finn orchestration-noder med matchende `metadata.trigger.event`
|
||||||
|
/// 2. For hver: sjekk `observes`-edge (eksplisitt kobling)
|
||||||
|
/// 3. For hver: evaluer `conditions` mot trigger-konteksten
|
||||||
|
/// 4. Legg matchende i jobbkøen som `orchestrate`-jobb
|
||||||
|
async fn evaluate_triggers(db: &PgPool, ctx: &TriggerContext) -> Result<(), String> {
|
||||||
|
// Steg 1: Finn alle orchestration-noder med matchende trigger-event.
|
||||||
|
// Bruker den funksjonelle indeksen idx_nodes_orchestration_trigger_event.
|
||||||
|
let candidates = sqlx::query_as::<_, OrchestrationMatch>(
|
||||||
|
r#"
|
||||||
|
SELECT id, metadata
|
||||||
|
FROM nodes
|
||||||
|
WHERE node_kind = 'orchestration'
|
||||||
|
AND metadata -> 'trigger' ->> 'event' = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(&ctx.event)
|
||||||
|
.fetch_all(db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Feil ved henting av orchestration-kandidater: {e}"))?;
|
||||||
|
|
||||||
|
if candidates.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
event = %ctx.event,
|
||||||
|
candidates = candidates.len(),
|
||||||
|
"Fant orchestration-kandidater for trigger"
|
||||||
|
);
|
||||||
|
|
||||||
|
for candidate in &candidates {
|
||||||
|
match should_trigger(db, candidate, ctx).await {
|
||||||
|
Ok(true) => {
|
||||||
|
enqueue_orchestration(db, candidate, ctx).await?;
|
||||||
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
tracing::debug!(
|
||||||
|
orchestration_id = %candidate.id,
|
||||||
|
"Orchestration matchet ikke betingelser, hoppes over"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(
|
||||||
|
orchestration_id = %candidate.id,
|
||||||
|
error = %e,
|
||||||
|
"Feil ved evaluering av orchestration-betingelser"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Avgjør om en orchestration-node skal trigges, basert på:
|
||||||
|
/// 1. `observes`-edge — eksplisitt kobling til triggerende node
|
||||||
|
/// 2. `conditions` — implisitt matching mot trigger-kontekst
|
||||||
|
///
|
||||||
|
/// Logikk (fra docs/concepts/orkestrering.md § 9):
|
||||||
|
/// - Hvis orchestration har `observes`-edges: trigger KUN hvis en peker til triggerende node
|
||||||
|
/// - Hvis ingen `observes`-edges: evaluer `conditions` (implisitt matching)
|
||||||
|
async fn should_trigger(
|
||||||
|
db: &PgPool,
|
||||||
|
candidate: &OrchestrationMatch,
|
||||||
|
ctx: &TriggerContext,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
// Samle alle relevante node-IDer fra konteksten
|
||||||
|
let relevant_ids: Vec<Uuid> = [ctx.node_id, ctx.source_id, ctx.target_id]
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| *id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if relevant_ids.is_empty() {
|
||||||
|
// Ingen node-kontekst å matche mot — kan ikke evaluere
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sjekk om orchestration har noen `observes`-edges overhodet
|
||||||
|
let observes_count = sqlx::query_scalar::<_, i64>(
|
||||||
|
"SELECT COUNT(*) FROM edges WHERE source_id = $1 AND edge_type = 'observes'",
|
||||||
|
)
|
||||||
|
.bind(candidate.id)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Feil ved sjekk av observes-edges: {e}"))?;
|
||||||
|
|
||||||
|
if observes_count > 0 {
|
||||||
|
// Eksplisitt modus: sjekk om noen observes-edge peker til en relevant node
|
||||||
|
let observes_match = sqlx::query_scalar::<_, i64>(
|
||||||
|
r#"
|
||||||
|
SELECT COUNT(*) FROM edges
|
||||||
|
WHERE source_id = $1
|
||||||
|
AND edge_type = 'observes'
|
||||||
|
AND target_id = ANY($2)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(candidate.id)
|
||||||
|
.bind(&relevant_ids)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Feil ved matching av observes-edges: {e}"))?;
|
||||||
|
|
||||||
|
return Ok(observes_match > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implisitt modus: evaluer conditions
|
||||||
|
evaluate_conditions(db, candidate, ctx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluerer `metadata.trigger.conditions` mot trigger-konteksten.
|
||||||
|
///
|
||||||
|
/// Kjente betingelser:
|
||||||
|
/// - `node_kind` — matcher node_kind fra konteksten
|
||||||
|
/// - `has_trait` — noden tilhører en samling med denne traiten
|
||||||
|
/// - `has_tag` — noden har en `tagged`-edge med denne verdien
|
||||||
|
/// - `edge_type` — matcher edge_type fra konteksten (for edge-events)
|
||||||
|
///
|
||||||
|
/// Alle betingelser må matche (AND-logikk).
|
||||||
|
async fn evaluate_conditions(
|
||||||
|
db: &PgPool,
|
||||||
|
candidate: &OrchestrationMatch,
|
||||||
|
ctx: &TriggerContext,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
let conditions = match candidate
|
||||||
|
.metadata
|
||||||
|
.get("trigger")
|
||||||
|
.and_then(|t| t.get("conditions"))
|
||||||
|
.and_then(|c| c.as_object())
|
||||||
|
{
|
||||||
|
Some(c) => c,
|
||||||
|
None => return Ok(true), // Ingen betingelser = alltid match
|
||||||
|
};
|
||||||
|
|
||||||
|
for (key, value) in conditions {
|
||||||
|
let val_str = match value.as_str() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => continue, // Ignorer ikke-streng-verdier
|
||||||
|
};
|
||||||
|
|
||||||
|
let matched = match key.as_str() {
|
||||||
|
"node_kind" => ctx.node_kind.as_deref() == Some(val_str),
|
||||||
|
|
||||||
|
"edge_type" => ctx.edge_type.as_deref() == Some(val_str),
|
||||||
|
|
||||||
|
"has_trait" => {
|
||||||
|
if let Some(node_id) = ctx.node_id {
|
||||||
|
has_trait(db, node_id, val_str).await?
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"has_tag" => {
|
||||||
|
if let Some(node_id) = ctx.node_id {
|
||||||
|
has_tag(db, node_id, val_str).await?
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
tracing::debug!(
|
||||||
|
condition = key.as_str(),
|
||||||
|
"Ukjent trigger-betingelse, ignoreres"
|
||||||
|
);
|
||||||
|
true // Ukjente betingelser blokkerer ikke
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sjekker om en node tilhører en samling som har en bestemt trait.
|
||||||
|
/// Følger `belongs_to`-edge → samlingsnodiens `metadata.traits`.
|
||||||
|
async fn has_trait(db: &PgPool, node_id: Uuid, trait_name: &str) -> Result<bool, String> {
|
||||||
|
// Finn samlinger som noden tilhører, sjekk om noen har traiten
|
||||||
|
let count = sqlx::query_scalar::<_, i64>(
|
||||||
|
r#"
|
||||||
|
SELECT COUNT(*) FROM edges e
|
||||||
|
JOIN nodes n ON n.id = e.target_id
|
||||||
|
WHERE e.source_id = $1
|
||||||
|
AND e.edge_type = 'belongs_to'
|
||||||
|
AND n.node_kind = 'collection'
|
||||||
|
AND n.metadata -> 'traits' ? $2
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(node_id)
|
||||||
|
.bind(trait_name)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Feil ved has_trait-sjekk: {e}"))?;
|
||||||
|
|
||||||
|
Ok(count > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sjekker om en node har en `tagged`-edge med en bestemt verdi.
|
||||||
|
async fn has_tag(db: &PgPool, node_id: Uuid, tag: &str) -> Result<bool, String> {
|
||||||
|
let count = sqlx::query_scalar::<_, i64>(
|
||||||
|
r#"
|
||||||
|
SELECT COUNT(*) FROM edges
|
||||||
|
WHERE source_id = $1
|
||||||
|
AND edge_type = 'tagged'
|
||||||
|
AND metadata ->> 'tag' = $2
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(node_id)
|
||||||
|
.bind(tag)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Feil ved has_tag-sjekk: {e}"))?;
|
||||||
|
|
||||||
|
Ok(count > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Legger en matchende orchestration i jobbkøen.
|
||||||
|
async fn enqueue_orchestration(
|
||||||
|
db: &PgPool,
|
||||||
|
candidate: &OrchestrationMatch,
|
||||||
|
ctx: &TriggerContext,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let payload = serde_json::json!({
|
||||||
|
"orchestration_id": candidate.id.to_string(),
|
||||||
|
"trigger_event": ctx.event,
|
||||||
|
"trigger_context": {
|
||||||
|
"node_id": ctx.node_id.map(|id| id.to_string()),
|
||||||
|
"node_kind": ctx.node_kind,
|
||||||
|
"edge_type": ctx.edge_type,
|
||||||
|
"source_id": ctx.source_id.map(|id| id.to_string()),
|
||||||
|
"target_id": ctx.target_id.map(|id| id.to_string()),
|
||||||
|
"op": ctx.op,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prioritet 5 = normal (mellom batch-jobber og brukerforespørsler)
|
||||||
|
crate::jobs::enqueue(db, "orchestrate", payload, None, 5)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Feil ved enqueue av orchestration-jobb: {e}"))?;
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
orchestration_id = %candidate.id,
|
||||||
|
event = %ctx.event,
|
||||||
|
"Orchestration trigget — jobb lagt i kø"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -289,10 +289,55 @@ async fn pg_listen_loop(db: &PgPool, ws: &WsBroadcast) -> Result<(), sqlx::Error
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Trigger-evaluering: sjekk om orchestration-noder matcher eventet.
|
||||||
|
// Spawner async — blokkerer ikke NOTIFY-loopen.
|
||||||
|
if let Some(trigger_ctx) = build_trigger_context(&event) {
|
||||||
|
crate::orchestration_trigger::spawn_trigger_evaluation(db.clone(), trigger_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
let _ = ws.tx.send(event);
|
let _ = ws.tx.send(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mapper et BroadcastEvent til en TriggerContext for orchestration-evaluering.
|
||||||
|
/// Returnerer None for events som ikke er relevante for triggere (access, mixer).
|
||||||
|
fn build_trigger_context(event: &BroadcastEvent) -> Option<crate::orchestration_trigger::TriggerContext> {
|
||||||
|
match event {
|
||||||
|
BroadcastEvent::NodeChanged { op, id, kind, .. } => {
|
||||||
|
let trigger_event = match op.as_str() {
|
||||||
|
"INSERT" => "node.created",
|
||||||
|
_ => return None, // UPDATE/DELETE er ikke trigger-events foreløpig
|
||||||
|
};
|
||||||
|
Some(crate::orchestration_trigger::TriggerContext {
|
||||||
|
event: trigger_event.to_string(),
|
||||||
|
node_id: Some(*id),
|
||||||
|
node_kind: Some(kind.clone()),
|
||||||
|
edge_type: None,
|
||||||
|
source_id: None,
|
||||||
|
target_id: None,
|
||||||
|
op: op.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
BroadcastEvent::EdgeChanged { op, source_id, target_id, edge_type, .. } => {
|
||||||
|
let trigger_event = match op.as_str() {
|
||||||
|
"INSERT" => "edge.created",
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(crate::orchestration_trigger::TriggerContext {
|
||||||
|
event: trigger_event.to_string(),
|
||||||
|
node_id: Some(*source_id), // source som primær node-kontekst
|
||||||
|
node_kind: None,
|
||||||
|
edge_type: Some(edge_type.clone()),
|
||||||
|
source_id: Some(*source_id),
|
||||||
|
target_id: Some(*target_id),
|
||||||
|
op: op.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Access- og mixer-events er ikke orchestration-triggere
|
||||||
|
BroadcastEvent::AccessChanged { .. } | BroadcastEvent::MixerChannelChanged { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Berikelse: hent full rad fra PG etter NOTIFY
|
// Berikelse: hent full rad fra PG etter NOTIFY
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
13
migrations/021_orchestration_trigger_index.sql
Normal file
13
migrations/021_orchestration_trigger_index.sql
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
-- Migration 021: Indeks for effektiv trigger-lookup på orchestration-noder.
|
||||||
|
--
|
||||||
|
-- Portvokteren trenger å finne alle orchestration-noder som matcher et
|
||||||
|
-- gitt trigger-event (f.eks. "node.created") raskt ved hver NOTIFY.
|
||||||
|
-- Denne indeksen gjør det til en B-tree-lookup i stedet for full tabellscan.
|
||||||
|
--
|
||||||
|
-- Ref: docs/concepts/orkestrering.md § 5 "Strukturert trigger"
|
||||||
|
|
||||||
|
-- Funksjonell indeks: trekker ut trigger.event fra metadata-JSONB,
|
||||||
|
-- begrenset til orchestration-noder.
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_nodes_orchestration_trigger_event
|
||||||
|
ON nodes ((metadata -> 'trigger' ->> 'event'))
|
||||||
|
WHERE node_kind = 'orchestration';
|
||||||
3
tasks.md
3
tasks.md
|
|
@ -317,8 +317,7 @@ med fritekst-instruksjoner som boten utfører via function calling. Strukturerte
|
||||||
automatisk eskalering av intelligens ved feil, kompilering av velprøvde mønstre.
|
automatisk eskalering av intelligens ved feil, kompilering av velprøvde mønstre.
|
||||||
|
|
||||||
- [x] 24.1 Orchestration node-type: legg til `orchestration` i maskinrommets node-validering. Metadata-skjema: `trigger` (event + conditions), `executor`, `intelligence`, `effort`, `compiled`, `pipeline`. Valider trigger-events mot kjent liste.
|
- [x] 24.1 Orchestration node-type: legg til `orchestration` i maskinrommets node-validering. Metadata-skjema: `trigger` (event + conditions), `executor`, `intelligence`, `effort`, `compiled`, `pipeline`. Valider trigger-events mot kjent liste.
|
||||||
- [~] 24.2 Trigger-evaluering i portvokteren: ved node/edge-events, sjekk om noen `orchestration`-noder matcher triggeren. Effektiv lookup (indeksert på `metadata.trigger.event`). Ingen LLM-kall for trigger-matching.
|
- [x] 24.2 Trigger-evaluering i portvokteren: ved node/edge-events, sjekk om noen `orchestration`-noder matcher triggeren. Effektiv lookup (indeksert på `metadata.trigger.event`). Ingen LLM-kall for trigger-matching.
|
||||||
> Påbegynt: 2026-03-18T16:46
|
|
||||||
- [ ] 24.3 Script-kompilator: parser menneskelig scriptspråk ("transkriber lydfilen (stor modell)") og kompilerer til tekniske CLI-kall. Matcher verb mot `cli_tool`-noders `aliases`, argumenter mot `args_hints`, variabler fra trigger-kontekst. Rust-stil kompileringsfeil med forslag.
|
- [ ] 24.3 Script-kompilator: parser menneskelig scriptspråk ("transkriber lydfilen (stor modell)") og kompilerer til tekniske CLI-kall. Matcher verb mot `cli_tool`-noders `aliases`, argumenter mot `args_hints`, variabler fra trigger-kontekst. Rust-stil kompileringsfeil med forslag.
|
||||||
- [ ] 24.4 cli_tool alias-metadata: utvid alle `cli_tool`-noder med `aliases` (norske verb) og `args_hints` (menneskelige argumenter → CLI-flagg). Seed for alle eksisterende verktøy.
|
- [ ] 24.4 cli_tool alias-metadata: utvid alle `cli_tool`-noder med `aliases` (norske verb) og `args_hints` (menneskelige argumenter → CLI-flagg). Seed for alle eksisterende verktøy.
|
||||||
- [ ] 24.5 Script-executor: vaktmesteren parser kompilert script og eksekverer steg sekvensielt via generisk dispatch. VED_FEIL-håndtering. Logger i `orchestration_log`.
|
- [ ] 24.5 Script-executor: vaktmesteren parser kompilert script og eksekverer steg sekvensielt via generisk dispatch. VED_FEIL-håndtering. Logger i `orchestration_log`.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue