Context Engine: Event Ingestion, Decision Making, and Template Synthesis
The Portal context engine (portal-context crate, v0.3.0) is the platform’s proactive intelligence layer — it ingests heterogeneous system events, normalizes them into a typed event model, extracts scored context with temporal decay, evaluates rule-based decisions, and synthesizes human-readable notifications through TOML templates rendered by Tera. The crate is #![forbid(unsafe_code)] and achieves thread safety entirely through parking_lot locks, atomic counters, and tokio channels. The single public entry point is portal/context/src/portal_context/mod.rs#L50-L65, which wires together 13 subsystems in a strict initialization order and exposes a 10-stage event processing pipeline.
Sources: portal/context/src/lib.rs#L1-L51, portal/context/Cargo.toml#L1-L9
Architecture: The 10-Stage Event Pipeline
Section titled “Architecture: The 10-Stage Event Pipeline”Every event entering the context engine flows through a deterministic pipeline inside portal/context/src/portal_context/pipeline.rs#L40-L122. Only ingestion errors (critical overflow) or shutdown state cause the pipeline to short-circuit; all other failures are logged but tolerated, preserving resilience. The following diagram traces the full data flow from raw event arrival through notification delivery:
flowchart TD
A[Raw ContextEvent] --> B["Step 1: EventIngestion<br/>normalize → dedup → buffer → dispatch"]
B --> C{Compositor<br/>event?}
C -->|Yes| D["Step 2: WorkspaceModel<br/>surface/focus handlers"]
C -->|No| E
D --> E["Step 3: ContextExtraction<br/>map → score → decay → hot store"]
E --> F["Step 4: SizeManager<br/>hot tier store"]
F --> G["Step 5: ContextQueryService<br/>broadcast update"]
G --> H["Step 6: DecisionEngine<br/>rules + DND + rate limit"]
H --> I["Step 7: TemplateSynthesizer<br/>Tera render + truncate"]
I --> J["Step 8: NotificationManager<br/>deliver + auto-dismiss"]
H --> K["Step 9: ProactiveManager<br/>battery/idle/calendar checks"]
K --> J
J --> L["Step 10: PrivacyManager<br/>audit log"]
B -.-> M["Telemetry<br/>lock-free counters"]
E -.-> M
H -.-> M
J -.-> M
The PortalContext struct holds all subsystems behind RwLock or Mutex guards, with a compile-time Send + Sync assertion ensuring no interior type violates these bounds. The portal/context/src/portal_context/constructor.rs#L45-L114 initializes subsystems in a dependency-ordered sequence: Config → Schema (SQLite) → SizeManager → WorkspaceModel → ContextExtraction → TemplateSynthesizer → DecisionEngine → NotificationManager → ProactiveManager → Telemetry → PrivacyManager → ContextQueryService → EventIngestion.
Sources: portal/context/src/portal_context/pipeline.rs#L40-L122, portal/context/src/portal_context/mod.rs#L1-L76, portal/context/src/portal_context/constructor.rs#L45-L114
Event Ingestion: Normalization, Dedup, Backpressure, Dispatch
Section titled “Event Ingestion: Normalization, Dedup, Backpressure, Dispatch”The ContextEvent Universal Currency
Section titled “The ContextEvent Universal Currency”All events from all sources are normalized into portal/context/src/events.rs#L243-L259 — the universal data structure that flows through the pipeline. It carries a unique EventId, a domain-specific ContextEventKind, the originating EventSource, a priority hint, a per-source monotonic sequence number, and a content_hash for deduplication.
The ContextEventKind enum wraps eight domain event types, each #[non_exhaustive] for forward-compatible deserialization:
| Domain | Enum | Example Variant | Priority Default |
|---|---|---|---|
| Compositor | CompositorEvent |
FocusGained { app_id, window_title, window_id } |
High |
| System | SystemEvent |
BatteryCritical { level_pct: u8 } |
Critical |
| Voice | VoiceEvent |
IntentClassified { intent, confidence, text, slots } |
High |
| Calendar | CalendarEvent |
EventStarting { event_id, title, minutes_until } |
Normal |
| Notification | NotificationEvent |
NotificationReceived { id, app_id, title, urgency } |
Urgency-mapped |
| Activity | ActivityEvent |
ExtendedFocus { app_id, duration_minutes } |
Normal |
| TimeTrigger | TimeTriggerEvent |
MorningBriefingTrigger |
Low |
| Permission | PermissionEvent |
PermissionGranted { capability } |
Normal |
Priority is not user-configurable — it is assigned from a fixed mapping in portal/context/src/event_ingestion/util.rs#L38-L69 based on event kind. Critical events (battery critical, critical-urgency notifications) receive EventPriority::Critical; compositor and voice events default to High; routine system and calendar events to Normal; time triggers and unknown events to Low.
Sources: portal/context/src/events.rs#L186-L259, portal/context/src/event_ingestion/util.rs#L38-L69
Pipeline Mechanics
Section titled “Pipeline Mechanics”The portal/context/src/event_ingestion/pipeline.rs#L27-L35 struct implements a five-stage synchronous pipeline within its portal/context/src/event_ingestion/pipeline.rs#L62-L186 method:
Stage 1 — Stats increment. A Mutex<IngestionStats> counter is atomically incremented for events_received. This provides diagnostic visibility into pipeline throughput.
Stage 2 — Dedup check. A content hash is computed from (source, kind) via DefaultHasher. Combined with the source, this produces a per-source dedup key. The dedup state (Mutex<HashMap<u64, DedupWindow>>) tracks the last hash, timestamp, and sequence per key. If an event arrives within dedup_window_ms (default 500ms) with an identical content hash, it is silently dropped. Additionally, sequence gaps are detected and logged — if sequence > expected, the gap is recorded in sequence_gaps_detected for at-most-once monitoring.
Stage 3 — Normalization. A new EventId is allocated atomically, priority is assigned, and the event is stamped with its Utc::now() timestamp and computed content hash.
Stage 4 — Backpressure buffering. The portal/context/src/event_ingestion/types.rs#L42-L95 uses a dual-lane ring buffer design. Normal events push into a VecDeque with normal_capacity slots; when full, the oldest normal event is evicted (ring behavior) and push_normal returns false to indicate the drop. Critical events push into a separate VecDeque with critical_capacity reserved slots — they never compete with normal events for space. When the critical lane is full, the oldest critical event is evicted and CriticalOverflow is returned (the only error path in the pipeline).
Stage 5 — Dispatch. Subscribers are notified via try_send on tokio mpsc channels. Each subscriber holds a filter (EventKindFilter) that can match by All, Domain, Priority, or Source. Dead subscribers whose channel buffer is full are collected and removed in reverse index order to preserve indices during removal.
| Configuration Parameter | Default | Config Key |
|---|---|---|
| Total buffer size | 128 | event_ingestion.backpressure_buffer_size |
| Reserved critical slots | 8 | event_ingestion.critical_reserved_slots |
| Dedup window | 500ms | event_ingestion.dedup_window_ms |
| Subscriber channel capacity | 64 | Hardcoded |
Sources: portal/context/src/event_ingestion/pipeline.rs#L27-L186, portal/context/src/event_ingestion/types.rs#L42-L95, portal/context/config/context.toml#L11-L22
PCP Push Bridge: External Event Translation
Section titled “PCP Push Bridge: External Event Translation”The portal/context/src/pcp_bridge.rs#L70-L196 translates PCP (Portal Capability Protocol) push events into ContextEvents. It subscribes to a PushEventBus with an EventFilter, runs a dedicated tokio single-threaded runtime on a background thread, and calls an on_event callback for each translated event. The translation logic in portal/context/src/pcp_bridge.rs#L206-L260 maps CapabilityEventType variants: Registered → PermissionGranted, Unregistered → PermissionRevoked, StateChanged → domain-specific mapping (compositor workspace changes, system battery levels, calendar changes), ExecutionFailed → UnusualActivity. The bridge is not yet wired into PortalContext — it is gated for incremental adoption via a feature flag.
Sources: portal/context/src/pcp_bridge.rs#L70-L260, portal/context/src/lib.rs#L31-L34
Context Extraction: Scoring, Decay, and Hot Store
Section titled “Context Extraction: Scoring, Decay, and Hot Store”Event-to-Context Mapping
Section titled “Event-to-Context Mapping”portal/context/src/context_extraction/pipeline.rs#L32-L35 transforms ContextEvent instances into typed ExtractedContext entries. The mapping logic in portal/context/src/context_extraction/mapping.rs#L15-L31 dispatches on ContextEventKind, producing zero or more (ContextKey, ContextData, Option<app_id>) tuples. Each tuple becomes a scored ExtractedContext with relevance, importance, temporal, and combined scores:
| Event Source | ContextKey | ContextData Variant |
|---|---|---|
CompositorEvent::FocusGained |
CurrentFocus |
CurrentFocus { app_name, app_id, window_title, ... } |
SystemEvent::BatteryLevel |
BatteryState |
BatteryState { level_pct, charging, trend, ... } |
SystemEvent::NetworkChanged |
NetworkState |
NetworkState { connected, ssid, ... } |
CalendarEvent::EventStarting |
UpcomingCalendar |
UpcomingCalendar { events, next_event_in } |
NotificationEvent::NotificationReceived |
PendingNotifications |
PendingNotifications { count, critical_count, latest } |
VoiceEvent::IntentClassified |
VoiceHistory |
VoiceHistory { recent_intents, last_interaction } |
| Any event timestamp | TimeContext |
TimeContext { hour, day_of_week, is_work_hours, ... } |
Sources: portal/context/src/context_extraction/mapping.rs#L15-L31, portal/context/src/types.rs#L77-L173
Relevance Scoring and Temporal Decay
Section titled “Relevance Scoring and Temporal Decay”Relevance scoring follows a multiplicative model defined in portal/context/src/context_extraction/relevance.rs#L13-L33 and portal/context/src/context_extraction/pipeline.rs#L134-L169:
combined_score = clamp(relevance × importance × temporal_factor, 0.0, 1.0)relevance = clamp(base_relevance(key) × focus_boost × user_config_boost, 0.0, 1.0)temporal_factor = e^(-λ × elapsed_seconds / ttl_seconds) where λ = 1.0The base relevance table assigns inherent importance to each context type — CurrentFocus scores highest at 0.9, followed by ExtendedFocus and UnusualActivity at 0.8, while NetworkState sits lowest at 0.2. The focus boost multiplies relevance by 1.2 when the context’s relation_app_id matches the currently focused application, creating a preference for context about what the user is actively doing.
The temporal decay model uses exponential decay with λ=1.0, meaning a context entry’s influence halves roughly at TTL × ln(2) elapsed. Entries whose temporal_factor drops below 0.01 are considered expired and pruned. The portal/context/src/context_extraction/relevance.rs#L39-L58 table assigns lifetimes from 5 minutes (CurrentFocus, TimeContext) to 12 hours (EndOfDaySummary).
| ContextKey | Base Relevance | Default TTL |
|---|---|---|
CurrentFocus |
0.9 | 5 min |
ExtendedFocus |
0.8 | 30 min |
UnusualActivity |
0.8 | 15 min |
UpcomingCalendar |
0.7 | 2 hours |
PendingNotifications |
0.7 | 15 min |
BatteryState |
0.6 | 30 min |
WorkspaceState |
0.5 | 10 min |
NetworkState |
0.2 | 30 min |
The hot store caps at 2,048 entries; when exceeded, the oldest entries are drained from the front of the Vec.
Sources: portal/context/src/context_extraction/relevance.rs#L13-L58, portal/context/src/context_extraction/pipeline.rs#L134-L210
Decision Engine: Rules, Suppression, and Rate Limiting
Section titled “Decision Engine: Rules, Suppression, and Rate Limiting”Rule Evaluation Pipeline
Section titled “Rule Evaluation Pipeline”The portal/context/src/decision_engine/engine.rs#L24-L32 evaluates ContextEvents against a vector of DecisionRules. Each evaluation pass in portal/context/src/decision_engine/engine.rs#L60-L161 applies five filtering stages per rule:
flowchart LR
A[Rule] --> B{enabled?}
B -->|No| Z[Skip]
B -->|Yes| C{trigger_key<br/>matches context?}
C -->|No| Z
C -->|Yes| D{trigger_condition<br/>satisfied?}
D -->|No| Z
D -->|Yes| E{debounce /<br/>cooldown /<br/>max_per_hour<br/>windows?}
E -->|Blocked| Z
E -->|OK| F["Record fire"]
F --> G{DND active<br/>AND priority < High?}
G -->|Suppressed| H["Output suppressed:<br/>dnd_suppressed"]
G -->|No| I{Rate limit<br/>passed?}
I -->|No| J["Output suppressed:<br/>rate_limit_suppressed"]
I -->|Yes| K["DecisionOutput<br/>emitted"]
H --> K
J --> K
Trigger matching works in two layers. First, the rule’s trigger_key (a ContextKey) must match at least one entry in the extracted context slice. Second, if a trigger_condition is present, it is evaluated via portal/context/src/decision_engine/trigger.rs#L11-L51, which supports six condition types: LessThan, GreaterThan, Equals, IsPresent, AllOf (logical AND), and AnyOf (logical OR). Field extraction traverses dot-separated paths through ContextData variants using JSON serialization and path navigation.
Per-rule state is tracked in portal/context/src/decision_engine/state.rs#L8-L30, which maintains last_fired_at, a fire counter, and a recent_fires vector pruned to a one-hour window for max_per_hour enforcement. Debounce prevents re-triggering within a configurable window; cooldown enforces a minimum gap between successive fires; max_per_hour caps total fires in any rolling hour.
Suppressed outputs are still returned — they carry suppressed: true with a suppression_reason string, preserving observability without delivering to the user.
Sources: portal/context/src/decision_engine/engine.rs#L60-L161, portal/context/src/decision_engine/trigger.rs#L11-L51, portal/context/src/decision_engine/state.rs#L8-L30
Do Not Disturb and Token-Bucket Rate Limiting
Section titled “Do Not Disturb and Token-Bucket Rate Limiting”DND logic in portal/context/src/decision_engine/engine.rs#L167-L172 and portal/context/src/decision_engine/engine.rs#L220-L226 handles midnight-wrapping windows. When dnd_start_hour > dnd_end_hour (e.g., 23 > 8), the active window is hour >= start OR hour < end. When start == end, DND is active 24 hours. Only rules with priority < High are suppressed — Critical and High rules always break through.
Rate limiting uses per-channel portal/context/src/decision_engine/state.rs#L33-L78 instances. Each bucket has a capacity and refill_rate (both equal to the configured max_per_hour). Tokens refill continuously based on elapsed time since last refill. For OutputChannel::Both, the engine attempts to consume from both visual and voice buckets atomically — if one succeeds and the other fails, it refunds the successful one to avoid token leakage.
| Rate Limit Parameter | Default | Affects |
|---|---|---|
voice_max_per_hour |
8 | Voice channel token bucket |
visual_max_per_hour |
30 | Visual channel token bucket |
dnd_enabled |
true | Suppresses < High priority |
dnd_start_hour |
23 | DND window start |
dnd_end_hour |
8 | DND window end |
Sources: portal/context/src/decision_engine/engine.rs#L163-L197, portal/context/src/decision_engine/state.rs#L33-L78, portal/context/config/context.toml#L50-L70
Decision Actions and Default Rules
Section titled “Decision Actions and Default Rules”The portal/context/src/decision_engine/types.rs#L54-L68 enum defines three possible outputs. Notify emits a user-facing notification through the notification manager. ExecutePcpAction is always suggestion-only — the engine never auto-executes PCP capabilities; the output is a recommendation that a user or downstream system must explicitly accept (this is a deliberate design constraint, labeled V3.0 #5 in the codebase). LogOnly records the decision for telemetry without any user-visible output.
Six default rules ship in portal/context/src/default_rules.rs#L17-L26, each with carefully tuned debounce/cooldown/max_per_hour values:
| Rule ID | Trigger Key | Condition | Priority | Debounce | Cooldown | Max/Hr | Template |
|---|---|---|---|---|---|---|---|
battery-low |
BatteryState |
level < 15% AND not charging | Normal | 60s | 5 min | 4 | battery-low |
battery-critical |
BatteryState |
level < 5% | Critical | 30s | 2 min | 10 | battery-critical |
meeting-soon |
UpcomingCalendar |
minutes_until < 10 | High | 60s | 5 min | 6 | meeting-soon |
extended-focus |
CurrentFocus |
focus_duration > 120 min | Low | 5 min | 30 min | 2 | extended-focus |
network-restored |
NetworkState |
connected == true | Normal | 30s | 10 min | 4 | network-restored |
welcome-back |
IdleState |
idle_duration > 30 AND idle == false | Low | 60s | 30 min | 2 | welcome-back |
Sources: portal/context/src/decision_engine/types.rs#L54-L68, portal/context/src/default_rules.rs#L17-L198, portal/context/src/decision_engine/mod.rs#L1-L25
Template Synthesis: TOML Templates and Tera Rendering
Section titled “Template Synthesis: TOML Templates and Tera Rendering”Template Format and Loading
Section titled “Template Format and Loading”Templates are TOML files parsed into a portal/context/src/template_synthesis/toml_format.rs#L36-L49 intermediate representation, then built into a portal/context/src/template_synthesis/types.rs#L74-L91 struct. The loaded Template carries five components: the raw template_text (with Tera {{variable}} and {% if condition %} syntax), a variables map (binding names to context keys + field paths), a conditions vector (parsed from {% if %} blocks), output constraints (max_length, tone, channel), and an optional fallback_text.
Here is the battery-low template as a concrete example:
id = "battery-low"version = 3template_text = "Battery at {{battery_level}}%. You should find a charger soon."fallback_text = "Battery is running low. Find a charger."
[variables.battery_level]context_key = "battery_state"field = "level_pct"
[conditions.low_check]type = "LessThan"field = "level_pct"value = 15
[output]max_length = 200tone = "Helpful"channel = "Visual"The synthesizer enforces a maximum template version of 10. During loading in portal/context/src/template_synthesis/synthesizer.rs#L59-L87, templates are parsed via toml::from_str, condition names are extracted from the template text (scanning for {% if name %}), and each condition is resolved from the TOML [conditions.*] sections. The resulting template text is registered with Tera via add_raw_template, enabling rendering.
Sources: portal/context/src/template_synthesis/toml_format.rs#L36-L124, portal/context/src/template_synthesis/synthesizer.rs#L59-L158, portal/context/templates/battery-low.toml#L1-L22
Synthesis: Variable Resolution and Conditional Evaluation
Section titled “Synthesis: Variable Resolution and Conditional Evaluation”The portal/context/src/template_synthesis/synthesis.rs#L48-L87 method performs a three-phase rendering:
Phase 1 — Variable resolution. Each variable binding in the template is resolved by finding the ExtractedContext entry matching binding.context_key, then extracting the named field via portal/context/src/template_synthesis/field_extraction.rs#L9-L181. This function contains an exhaustive match on every ContextData variant, extracting typed fields as strings. For example, BatteryState.level_pct returns the u8 as a string, while UpcomingCalendar.next_event_in formats the duration as "N minutes".
Phase 2 — Condition evaluation. Each conditional section is evaluated against context via portal/context/src/template_synthesis/synthesis.rs#L129-L182. The same six condition types (LessThan, GreaterThan, Equals, IsPresent, AllOf, AnyOf) used by the decision engine’s trigger evaluation are available. The boolean result is inserted into the Tera context, controlling which {% if %} blocks render.
Phase 3 — Tera render + truncation. The Tera template is rendered with the assembled context. If the resulting text exceeds output.max_length, it is truncated at the nearest word boundary via truncate_at_word_boundary to avoid cutting mid-word. If the rendered text is empty or whitespace-only, the fallback_text is substituted. The final SynthesizedOutput carries the rendered text, template ID, tone hint, channel, and a truncated flag.
The welcome-back template demonstrates conditional composition with multiple {% if %} blocks:
template_text = "Welcome back.{% if with_notifications %} You have {{notification_count}} unread messages.{% endif %}{% if with_meeting %} You have a meeting in {{next_event_in}}.{% endif %}"Sources: portal/context/src/template_synthesis/synthesis.rs#L48-L183, portal/context/src/template_synthesis/field_extraction.rs#L9-L181, portal/context/templates/welcome-back.toml#L1-L30
Hot-Reload with Version Compatibility
Section titled “Hot-Reload with Version Compatibility”The portal/context/src/template_synthesis/synthesizer.rs#L21-L26 supports runtime template updates through portal/context/src/template_synthesis/hot_reload.rs#L22-L64 and portal/context/src/template_synthesis/hot_reload.rs#L72-L118. The file watcher uses notify::RecommendedWatcher with a 200ms poll interval in non-recursive mode. On reload, the new template’s version is checked against the existing registered version — the system accepts same or lower versions but rejects higher ones, returning TemplateVersionMismatch. This prevents forward-incompatible template changes from silently breaking the synthesis pipeline. The re-registration calls Tera::add_raw_template, which overwrites any existing template with the same ID.
Ten templates ship in the portal/context/templates/ directory:
| Template | Tone | Max Length | Channel | Condition Example |
|---|---|---|---|---|
battery-low |
Helpful | 200 | Visual | level_pct < 15 |
battery-critical |
Urgent | 100 | Visual | level_pct < 5 |
meeting-soon |
Helpful | 200 | Visual | minutes_until < 10 |
welcome-back |
Caring | 200 | Visual | count > 0 + IsPresent |
extended-focus |
Gentle | 200 | Visual | duration > 120 |
network-restored |
Neutral | 150 | Visual | connected == true |
morning-briefing |
Caring | 500 | Visual | IsPresent (3 conditions) |
end-of-day-summary |
Reflective | 500 | Visual | Multiple IsPresent |
post-meeting-prompt |
Casual | 300 | Visual | IsPresent |
unusual-activity |
Cautious | 200 | Visual | IsPresent |
Sources: portal/context/src/template_synthesis/hot_reload.rs#L22-L146, portal/context/src/template_synthesis/synthesizer.rs#L21-L26, portal/context/src/template_synthesis/types.rs#L74-L106
Supporting Subsystems
Section titled “Supporting Subsystems”Three-Tier Size Management
Section titled “Three-Tier Size Management”The portal/context/src/size_management/types.rs#L24-L63 implements a three-tier storage hierarchy for context persistence: Hot tier (in-memory LRU cache, 50MB default) for frequently accessed entries, Warm tier (SQLite database, 500MB) for structured queries, and Cold tier (zstd-compressed archives, 10GB, 21-day retention) for long-term storage. A maintenance cycle demotes entries from hot→warm→cold→purge based on age and size thresholds. During PortalContext::shutdown, a full maintenance cycle runs to flush all tiers.
Privacy, Consent, and Audit
Section titled “Privacy, Consent, and Audit”The portal/context/src/privacy/mod.rs#L31-L45 tracks every data access in an in-memory audit log (capped at 4,096 entries), manages user consent settings, and enforces data retention policies against the SQLite store. The record_access_auto method logs automated (non-user-initiated) accesses — the event pipeline calls this in Step 10 for every processed event. Anonymized export replaces contact-like capitalized words with [CONTACT_N] placeholders.
Proactive Intelligence
Section titled “Proactive Intelligence”The portal/context/src/proactive/mod.rs#L19 runs ten check types on configurable intervals, from UpcomingEventCheck (every 60s) to MorningBriefing and EndOfDaySummary (every hour, time-gated). Checks are triggered contextually by the pipeline — battery events trigger BatteryCheck, idle events trigger IdleCheck, calendar events trigger UpcomingEventCheck. Each check returns a ProactiveCheckResult with should_notify, title, body, and priority.
Lock-Free Telemetry
Section titled “Lock-Free Telemetry”portal/context/src/telemetry.rs#L61-L100 uses AtomicU64 counters for all metrics, ensuring recording never blocks. The ContextTelemetrySnapshot exposes 15 counters spanning the full pipeline: events received/deduplicated/dropped/dispatched, contexts extracted, decisions evaluated/suppressed (DND and rate-limit separately), notifications delivered/dismissed, voice fallbacks, tier health (hot hit rate, eviction count, warm query latency p95, cold archive count and bytes).
Sources: portal/context/src/size_management/types.rs#L24-L63, portal/context/src/privacy/mod.rs#L31-L45, portal/context/src/proactive/types.rs#L1-L87, portal/context/src/telemetry.rs#L61-L100
Public API Summary
Section titled “Public API Summary”The PortalContext struct exposes four primary methods for consumers:
| Method | Returns | Purpose |
|---|---|---|
new(config_path: Option<&Path>) |
Result<Self> |
Construct with config file or defaults (in-memory SQLite) |
start() |
Result<()> |
Begin accepting events; sets running flag |
submit_event(event: ContextEvent) |
Result<()> |
Process a single event through all 10 pipeline stages |
query() |
RwLockReadGuard<ContextQueryService> |
Read access to stored context with key/time/app/tag filters |
telemetry() |
ContextTelemetrySnapshot |
Point-in-time snapshot of all pipeline counters |
shutdown() |
Result<()> |
Run full maintenance cycle, clear DB path, stop processing |
The ContextQueryService supports query_by_key (sorted by combined score), query_by_time_range, query_by_app, query_by_tags, current_focus snapshot, context_diff between entry sets, and real-time broadcast subscriptions filtered by keys, minimum relevance, or app ID.
Sources: portal/context/src/portal_context/runtime.rs#L17-L90, portal/context/src/context_query.rs#L75-L200
Next Steps
Section titled “Next Steps”- To understand how PCP capabilities feed events into this engine, read Portal Capability Protocol (PCP): Architecture, Registry, and Daemon.
- For the voice subsystem that both consumes and feeds context events, see Voice Pipeline: VAD, STT (sherpa-onnx), NLU (MiniLM), and TTS (Elara VITS).
- To explore how the Universal Launcher interacts with context for action discovery, continue to Universal Launcher: Action Registry, Fuzzy Search, and Plugin System.
- For the design token system that governs notification visual styling, see Shell, Style, and Design Token System.