rename the TIC system agent to event triage
Nightly Build / build (push) Successful in 7m16s

TIC said nothing about what the agent does, and named the wrong thing: the
tick belongs to the scheduler, which is generic and lives outside it. The
agent's only decision is whether an incoming event deserves an interruption
— it sorts, it never acts — so it is now event-triage, matching the
functional naming of the two memory lints.

- agents/tic/ -> agents/event-triage/, module tic/ -> event_triage/,
  TicManager -> EventTriageManager, TicConfig -> EventTriageConfig
- agent id and chat source: "tic" -> "event-triage"
- config keys: tic.* -> event_triage.*, and the config.yml section tic: ->
  event_triage: (greenfield: previously set values fall back to defaults)
- i18n en/it/fr: Event triage / Triage eventi / Tri des evenements; dropped
  the stale "TIC sessions" mention from the debug-pages description
- docs/system-agents.md, docs/index.md, docs/settings.md, CLAUDE.md, SKALD.md
This commit is contained in:
2026-07-28 21:59:37 +01:00
parent 0b793d56ae
commit 046f060fcd
50 changed files with 251 additions and 240 deletions
+2 -2
View File
@@ -160,7 +160,7 @@ impl Integrations {
/// injected by `main.rs`; `start_enabled()` runs later, from `WebFrontend`).
pub(super) fn build(rt: &Runtime, plugins: Vec<Arc<dyn Plugin>>) -> Self {
// The global runtime has no owner, so its notifications are not persisted:
// `mcp_events` is per-user and its only reader (TIC) runs per-user.
// `mcp_events` is per-user and its only reader (event triage) runs per-user.
let mcp = Arc::new(McpManager::new(
Arc::clone(&rt.db),
rt.shutdown_token.clone(),
@@ -397,7 +397,7 @@ impl Conversation {
chat_hub.register("web").await;
chat_hub.register("talk").await;
// TIC is deliberately absent: it is a system agent that runs *per user*,
// Event triage is deliberately absent: it is a system agent that runs *per user*,
// over that user's own events, sessions and hub. Building it here would
// bind it to the ownerless stack above (§19) — which is precisely the bug
// that made it inert. It is constructed by `wiring::spawn_system_agents`.
+1 -1
View File
@@ -113,7 +113,7 @@ impl Skald {
// Likewise the system-agent scheduler: it resolves a per-user runtime for
// each user it runs an agent for (blueprint §13).
spawn_system_agents(&skald, config.tic.clone());
spawn_system_agents(&skald, config.event_triage.clone());
Ok(skald)
}
+1 -1
View File
@@ -242,7 +242,7 @@ impl UserContextFactory {
user_shutdown.clone(),
"data",
// This user's connectors push into this user's `mcp_events`, which is
// what TIC reads on their behalf.
// what event triage reads on their behalf.
crate::mcp::EventLog::Persist,
));
// NOTE: per-user MCP elicitation (interactive connector login, §15) is
+15 -15
View File
@@ -15,7 +15,7 @@ use std::time::Duration;
use core_api::system_bus::{RecvError, SystemEvent};
use tracing::{info, warn};
use crate::config::{CoreConfig, TicConfig};
use crate::config::{CoreConfig, EventTriageConfig};
use crate::elicitation::ElicitationBridge;
use crate::system_agents::{self, AgentRunCtx, AgentScope, SystemAgent};
@@ -175,22 +175,22 @@ pub(super) fn spawn_user_lifecycle(skald: &Arc<super::Skald>) {
}
/// Spawns the **system-agent scheduler** — the one instance-wide timer behind
/// every background agent nobody asked for (TIC, the two memory lints).
/// every background agent nobody asked for (event triage, the two memory lints).
///
/// **One loop for all of them.** The agents differ by three orders of magnitude
/// in cadence — TIC every few minutes, a lint every week — which is exactly the
/// case that tempts a second loop. It stays one because the wake-up decides
/// nothing: [`base_tick`] only picks how often to *look*, and whether an agent
/// actually runs for a given user is [`system_agents::is_due`] against state in
/// that user's own database. Adding an agent therefore adds a registry entry,
/// never a task.
/// in cadence — event triage every few minutes, a lint every week — which is
/// exactly the case that tempts a second loop. It stays one because the wake-up
/// decides nothing: [`base_tick`] only picks how often to *look*, and whether an
/// agent actually runs for a given user is [`system_agents::is_due`] against
/// state in that user's own database. Adding an agent therefore adds a registry
/// entry, never a task.
///
/// **Due-ness is persisted, not counted from boot.** An in-memory deadline is
/// fine at TIC's scale but silently breaks a weekly agent: every restart re-arms
/// it, so on a machine rebooted every few days it would never fire once. Reading
/// the last attempt from `system_agent_state` makes a long interval survive
/// restarts, and has the pleasant side effect that a user who logs in after a
/// long absence is picked up on the next pass rather than a week later.
/// fine at event triage's scale but silently breaks a weekly agent: every restart
/// re-arms it, so on a machine rebooted every few days it would never fire once.
/// Reading the last attempt from `system_agent_state` makes a long interval
/// survive restarts, and has the pleasant side effect that a user who logs in
/// after a long absence is picked up on the next pass rather than a week later.
///
/// A user whose database is still locked is **skipped**, and that is the normal
/// case rather than an error: the pool is the unlock token (§9), so a user who
@@ -202,7 +202,7 @@ pub(super) fn spawn_user_lifecycle(skald: &Arc<super::Skald>) {
/// Spawned after `Skald` is fully built, like [`spawn_user_lifecycle`] and for
/// the same reason: it resolves each user's runtime through `Skald::user_context`.
/// The back-reference is [`std::sync::Weak`].
pub(super) fn spawn_system_agents(skald: &Arc<super::Skald>, tic_config: TicConfig) {
pub(super) fn spawn_system_agents(skald: &Arc<super::Skald>, event_triage_config: EventTriageConfig) {
let weak = Arc::downgrade(skald);
let shutdown = skald.rt.shutdown_token.clone();
let mut sys_rx = skald.rt.system_bus.subscribe();
@@ -211,7 +211,7 @@ pub(super) fn spawn_system_agents(skald: &Arc<super::Skald>, tic_config: TicConf
// `SystemAgent` impl — no loop of its own, which is the whole point: a second
// scheduler would be a fourth global bus in disguise.
let agents = system_agents::registry(
tic_config,
event_triage_config,
Arc::clone(&skald.rt.config),
Arc::clone(&skald.rt.db),
);