571 lines
13 KiB
Markdown
571 lines
13 KiB
Markdown
|
|
# GlyphOS Event System
|
||
|
|
|
||
|
|
**Status**: ✅ Complete and Tested
|
||
|
|
**Version**: 1.0.0
|
||
|
|
**Date**: May 20, 2026
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The **GlyphOS Event System** is a lightweight, in-process event bus that captures symbolic events from the Cognitive Kernel and LAIN cognition engine.
|
||
|
|
|
||
|
|
It enables:
|
||
|
|
- **Event-driven architecture** for GlyphOS applications
|
||
|
|
- **Cognition monitoring** through first-class events
|
||
|
|
- **Glyph activation tracking** via resonance changes
|
||
|
|
- **Clean separation** of concerns (publish-subscribe pattern)
|
||
|
|
|
||
|
|
## Event Types
|
||
|
|
|
||
|
|
The system defines five core event types:
|
||
|
|
|
||
|
|
```python
|
||
|
|
EventType = Literal[
|
||
|
|
"cognition.started", # Cognition execution starting
|
||
|
|
"cognition.completed", # Cognition execution finished
|
||
|
|
"glyph.activation.changed", # Glyph activation mode changed
|
||
|
|
"glyph.resonance.updated", # Glyph resonance metrics updated
|
||
|
|
"kernel.warmup.completed", # Kernel initialization complete
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
Application Layer
|
||
|
|
↓
|
||
|
|
on() / emit()
|
||
|
|
↓
|
||
|
|
┌─────────────────────────────────────────┐
|
||
|
|
│ Event Bus (Singleton) │
|
||
|
|
│ ├─ Subscriptions (type → handlers) │
|
||
|
|
│ ├─ History (all published events) │
|
||
|
|
│ └─ Publish mechanism │
|
||
|
|
└─────────────────────────────────────────┘
|
||
|
|
↑
|
||
|
|
Cognitive Kernel
|
||
|
|
├─ emit("kernel.warmup.completed")
|
||
|
|
├─ emit("cognition.started")
|
||
|
|
├─ emit("cognition.completed")
|
||
|
|
└─ emit("glyph.resonance.updated")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Module: `glyphos/events.py`
|
||
|
|
|
||
|
|
### EventBus Class
|
||
|
|
|
||
|
|
Main event bus implementation.
|
||
|
|
|
||
|
|
#### Constructor
|
||
|
|
|
||
|
|
```python
|
||
|
|
bus = EventBus()
|
||
|
|
```
|
||
|
|
|
||
|
|
Creates a new EventBus with no subscribers or history.
|
||
|
|
|
||
|
|
#### Methods
|
||
|
|
|
||
|
|
##### subscribe()
|
||
|
|
|
||
|
|
Register a handler for an event type.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def subscribe(self, event_type: EventType, handler: Callable[[Event], None]) -> None
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `event_type`: Event type to listen for
|
||
|
|
- `handler`: Callable that takes an Event and returns None
|
||
|
|
|
||
|
|
**Behavior:**
|
||
|
|
- Registers handler for this event type
|
||
|
|
- Multiple handlers can subscribe to the same type
|
||
|
|
- Handlers called in registration order
|
||
|
|
- Handler registration is idempotent (no duplicate registrations)
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```python
|
||
|
|
def on_warmup(event: Event):
|
||
|
|
print(f"Warmed up at {event['timestamp']}")
|
||
|
|
|
||
|
|
bus.subscribe("kernel.warmup.completed", on_warmup)
|
||
|
|
```
|
||
|
|
|
||
|
|
##### unsubscribe()
|
||
|
|
|
||
|
|
Remove a handler from an event type.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def unsubscribe(self, event_type: EventType, handler: Callable[[Event], None]) -> None
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `event_type`: Event type to unsubscribe from
|
||
|
|
- `handler`: The handler to remove
|
||
|
|
|
||
|
|
**Behavior:**
|
||
|
|
- Silently succeeds if handler not found
|
||
|
|
- Does not raise exceptions
|
||
|
|
|
||
|
|
##### publish()
|
||
|
|
|
||
|
|
Create an event, append to history, and invoke handlers.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def publish(self, event_type: EventType, payload: Dict[str, Any]) -> Event
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `event_type`: Type of event
|
||
|
|
- `payload`: Event data (arbitrary dict)
|
||
|
|
|
||
|
|
**Returns:**
|
||
|
|
```python
|
||
|
|
{
|
||
|
|
"type": event_type,
|
||
|
|
"timestamp": float, # time.time()
|
||
|
|
"payload": payload
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Behavior:**
|
||
|
|
- Attaches current timestamp
|
||
|
|
- Appends to event history
|
||
|
|
- Invokes all registered handlers in order
|
||
|
|
- Catches and silently suppresses handler exceptions
|
||
|
|
- Returns the published Event
|
||
|
|
|
||
|
|
##### get_history()
|
||
|
|
|
||
|
|
Retrieve recent events from history.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def get_history(self, event_type: Optional[EventType] = None, limit: int = 100) -> List[Event]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `event_type`: If provided, filter to only this type
|
||
|
|
- `limit`: Maximum number of events (default 100)
|
||
|
|
|
||
|
|
**Returns:**
|
||
|
|
- List of Event dicts (newest last)
|
||
|
|
- Empty list if no history
|
||
|
|
|
||
|
|
**Examples:**
|
||
|
|
```python
|
||
|
|
# All recent events (up to 100)
|
||
|
|
all_events = bus.get_history()
|
||
|
|
|
||
|
|
# All "cognition.completed" events
|
||
|
|
cognition_events = bus.get_history("cognition.completed")
|
||
|
|
|
||
|
|
# Last 10 events
|
||
|
|
recent = bus.get_history(limit=10)
|
||
|
|
|
||
|
|
# Last 5 glyph resonance events
|
||
|
|
last_glyphs = bus.get_history("glyph.resonance.updated", limit=5)
|
||
|
|
```
|
||
|
|
|
||
|
|
##### clear_history()
|
||
|
|
|
||
|
|
Clear all stored events.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def clear_history(self) -> None
|
||
|
|
```
|
||
|
|
|
||
|
|
**Use case**: Reset history for clean testing or memory management.
|
||
|
|
|
||
|
|
### Functional API
|
||
|
|
|
||
|
|
Module-level convenience functions.
|
||
|
|
|
||
|
|
#### get_event_bus()
|
||
|
|
|
||
|
|
Get or create the singleton event bus.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def get_event_bus() -> EventBus
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** Singleton EventBus instance
|
||
|
|
|
||
|
|
**Behavior:**
|
||
|
|
- Creates new EventBus on first call
|
||
|
|
- Returns same instance on subsequent calls
|
||
|
|
- No initialization required
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```python
|
||
|
|
bus = get_event_bus()
|
||
|
|
events = bus.get_history()
|
||
|
|
```
|
||
|
|
|
||
|
|
#### emit()
|
||
|
|
|
||
|
|
Publish an event on the global bus.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def emit(event_type: EventType, payload: Dict[str, Any]) -> Event
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `event_type`: Type of event
|
||
|
|
- `payload`: Event payload
|
||
|
|
|
||
|
|
**Returns:** Published Event
|
||
|
|
|
||
|
|
**Equivalent to:**
|
||
|
|
```python
|
||
|
|
get_event_bus().publish(event_type, payload)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```python
|
||
|
|
emit("kernel.warmup.completed", {
|
||
|
|
"glyph_stats": stats,
|
||
|
|
"startup_time": time.time()
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
#### on()
|
||
|
|
|
||
|
|
Subscribe to an event on the global bus.
|
||
|
|
|
||
|
|
```python
|
||
|
|
def on(event_type: EventType, handler: Callable[[Event], None]) -> None
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `event_type`: Event type to listen for
|
||
|
|
- `handler`: Handler callback
|
||
|
|
|
||
|
|
**Equivalent to:**
|
||
|
|
```python
|
||
|
|
get_event_bus().subscribe(event_type, handler)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```python
|
||
|
|
def handle_cognition_done(event: Event):
|
||
|
|
print(f"Cognition completed in {event['payload']['elapsed']}s")
|
||
|
|
|
||
|
|
on("cognition.completed", handle_cognition_done)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Events from Cognitive Kernel
|
||
|
|
|
||
|
|
The Cognitive Kernel emits events at key points in the execution pipeline.
|
||
|
|
|
||
|
|
### kernel.warmup.completed
|
||
|
|
|
||
|
|
Emitted when the kernel finishes warmup.
|
||
|
|
|
||
|
|
**When:** After `CognitiveKernel.warmup()` completes
|
||
|
|
|
||
|
|
**Payload:**
|
||
|
|
```python
|
||
|
|
{
|
||
|
|
"glyph_stats": {
|
||
|
|
"total_glyphs": 600,
|
||
|
|
"categories": [...],
|
||
|
|
"fields_present": [...],
|
||
|
|
"sample_ids": [...],
|
||
|
|
"loaded": True,
|
||
|
|
"load_path": "/path/to/glyphs.json",
|
||
|
|
},
|
||
|
|
"startup_time": float, # time.time()
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Use case:** Monitor kernel initialization, verify glyph registry loaded.
|
||
|
|
|
||
|
|
### cognition.started
|
||
|
|
|
||
|
|
Emitted at the start of GX execution.
|
||
|
|
|
||
|
|
**When:** At the beginning of `CognitiveKernel.execute_gx()`
|
||
|
|
|
||
|
|
**Payload:**
|
||
|
|
```python
|
||
|
|
{
|
||
|
|
"gx_path": str, # Path to .gx file
|
||
|
|
"mode": str, # "analyze", "debug", etc.
|
||
|
|
"context": Optional[dict], # User-provided context
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Use case:** Track when cognition starts, log execution attempts.
|
||
|
|
|
||
|
|
### cognition.completed
|
||
|
|
|
||
|
|
Emitted after GX execution completes.
|
||
|
|
|
||
|
|
**When:** After `execute_gx_path()` finishes and result is cached
|
||
|
|
|
||
|
|
**Payload:**
|
||
|
|
```python
|
||
|
|
{
|
||
|
|
"gx_path": str, # Path to .gx file
|
||
|
|
"mode": str, # Execution mode
|
||
|
|
"elapsed": float, # Seconds (from diagnostics)
|
||
|
|
"summary": str, # fused_symbol summary text
|
||
|
|
"glyph_resonance": dict, # Full resonance dict (if present)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Use case:** Track execution performance, analyze cognition results.
|
||
|
|
|
||
|
|
### glyph.resonance.updated
|
||
|
|
|
||
|
|
Emitted when glyph resonance metrics are available.
|
||
|
|
|
||
|
|
**When:** During `execute_gx()` if glyph_resonance present in diagnostics
|
||
|
|
|
||
|
|
**Condition:** Only emitted if `glyph_resonance["glyph_found"] == True`
|
||
|
|
|
||
|
|
**Payload:**
|
||
|
|
```python
|
||
|
|
{
|
||
|
|
"glyph_id": str, # "G001", "G042", etc.
|
||
|
|
"glyph_score": int, # Glyph strength metric
|
||
|
|
"glyph_resonance": { # Full resonance data
|
||
|
|
"activation_resonance": float, # 0.0-1.0
|
||
|
|
"frequency_resonance": float, # 0.0-1.0
|
||
|
|
"symbolic_resonance": float, # 0.0-1.0
|
||
|
|
"overall_resonance": float, # 0.0-1.0
|
||
|
|
"glyph_found": bool,
|
||
|
|
"glyph_id": str,
|
||
|
|
"glyph_score": int,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Use case:** Track which glyphs are being used, monitor resonance profiles.
|
||
|
|
|
||
|
|
## Usage Examples
|
||
|
|
|
||
|
|
### Basic Event Subscription
|
||
|
|
|
||
|
|
```python
|
||
|
|
from glyphos.events import on, emit
|
||
|
|
|
||
|
|
# Subscribe to an event
|
||
|
|
def on_cognition_done(event):
|
||
|
|
elapsed = event["payload"]["elapsed"]
|
||
|
|
print(f"Execution took {elapsed}s")
|
||
|
|
|
||
|
|
on("cognition.completed", on_cognition_done)
|
||
|
|
|
||
|
|
# Later, when cognition runs, handler is called automatically
|
||
|
|
```
|
||
|
|
|
||
|
|
### Monitoring Kernel State
|
||
|
|
|
||
|
|
```python
|
||
|
|
from glyphos.events import get_event_bus
|
||
|
|
|
||
|
|
bus = get_event_bus()
|
||
|
|
|
||
|
|
# Get all events since startup
|
||
|
|
history = bus.get_history()
|
||
|
|
print(f"Total events: {len(history)}")
|
||
|
|
|
||
|
|
# Get only cognition events
|
||
|
|
cognition_events = bus.get_history("cognition.completed")
|
||
|
|
for event in cognition_events:
|
||
|
|
print(f"Executed: {event['payload']['gx_path']}")
|
||
|
|
```
|
||
|
|
|
||
|
|
### Event-Driven Cognition App
|
||
|
|
|
||
|
|
```python
|
||
|
|
from glyphos.events import on, emit
|
||
|
|
from glyphos.cognitive_kernel import run_gx
|
||
|
|
|
||
|
|
# Set up listeners before running cognition
|
||
|
|
execution_log = []
|
||
|
|
|
||
|
|
on("cognition.started", lambda e: execution_log.append({
|
||
|
|
"type": "start",
|
||
|
|
"file": e["payload"]["gx_path"],
|
||
|
|
"time": e["timestamp"]
|
||
|
|
}))
|
||
|
|
|
||
|
|
on("cognition.completed", lambda e: execution_log.append({
|
||
|
|
"type": "done",
|
||
|
|
"elapsed": e["payload"]["elapsed"],
|
||
|
|
"time": e["timestamp"]
|
||
|
|
}))
|
||
|
|
|
||
|
|
on("glyph.resonance.updated", lambda e: execution_log.append({
|
||
|
|
"type": "glyph",
|
||
|
|
"id": e["payload"]["glyph_id"],
|
||
|
|
"resonance": e["payload"]["glyph_resonance"]["overall_resonance"]
|
||
|
|
}))
|
||
|
|
|
||
|
|
# Run cognition
|
||
|
|
result = run_gx("source.gx")
|
||
|
|
|
||
|
|
# Inspect log
|
||
|
|
for entry in execution_log:
|
||
|
|
print(entry)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Testing with Events
|
||
|
|
|
||
|
|
```python
|
||
|
|
from glyphos.events import get_event_bus, on
|
||
|
|
from glyphos.cognitive_kernel import CognitiveKernel
|
||
|
|
|
||
|
|
# Test fixture: collect emitted events
|
||
|
|
events = []
|
||
|
|
bus = get_event_bus()
|
||
|
|
|
||
|
|
def collector(event):
|
||
|
|
events.append(event)
|
||
|
|
|
||
|
|
bus.subscribe("kernel.warmup.completed", collector)
|
||
|
|
|
||
|
|
# Run test
|
||
|
|
kernel = CognitiveKernel()
|
||
|
|
kernel.warmup()
|
||
|
|
|
||
|
|
# Verify
|
||
|
|
assert len(events) == 1
|
||
|
|
assert events[0]["type"] == "kernel.warmup.completed"
|
||
|
|
assert events[0]["payload"]["glyph_stats"]["total_glyphs"] == 600
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
### Test Coverage
|
||
|
|
|
||
|
|
- **16 tests** in `tests/test_events.py`
|
||
|
|
- **100% pass rate**
|
||
|
|
|
||
|
|
### Test Categories
|
||
|
|
|
||
|
|
1. **EventBus Core** (9 tests)
|
||
|
|
- Initialization
|
||
|
|
- Subscribe/unsubscribe
|
||
|
|
- Publish mechanism
|
||
|
|
- History tracking and filtering
|
||
|
|
- History clearing
|
||
|
|
- Handler error handling
|
||
|
|
|
||
|
|
2. **Functional API** (3 tests)
|
||
|
|
- Singleton pattern
|
||
|
|
- emit() and on() functions
|
||
|
|
- Global bus management
|
||
|
|
|
||
|
|
3. **Kernel Integration** (4 tests)
|
||
|
|
- kernel.warmup.completed emission
|
||
|
|
- cognition.started emission
|
||
|
|
- cognition.completed emission
|
||
|
|
- glyph.resonance.updated emission
|
||
|
|
|
||
|
|
### Running Tests
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Run event system tests
|
||
|
|
python3 tests/test_events.py
|
||
|
|
|
||
|
|
# Run all tests (52 total)
|
||
|
|
python3 integration_tests/run_all_tests.py
|
||
|
|
```
|
||
|
|
|
||
|
|
## Design Principles
|
||
|
|
|
||
|
|
1. **Lightweight**: No external dependencies, minimal code
|
||
|
|
2. **In-Process**: All events stay in application memory
|
||
|
|
3. **Fire-and-Forget**: Handlers don't need to return
|
||
|
|
4. **Error Resilient**: Handler exceptions don't cascade
|
||
|
|
5. **Observable**: Full event history available for inspection
|
||
|
|
6. **Singleton**: One global bus for simplicity
|
||
|
|
|
||
|
|
## Performance
|
||
|
|
|
||
|
|
### Timing
|
||
|
|
|
||
|
|
| Operation | Duration |
|
||
|
|
|-----------|----------|
|
||
|
|
| emit() | <1ms |
|
||
|
|
| subscribe() | <1ms |
|
||
|
|
| publish() + handler call | <1ms |
|
||
|
|
| get_history() | <1ms |
|
||
|
|
|
||
|
|
### Memory
|
||
|
|
|
||
|
|
| Component | Usage |
|
||
|
|
|-----------|-------|
|
||
|
|
| Empty EventBus | ~100 bytes |
|
||
|
|
| Per event | ~200-500 bytes |
|
||
|
|
| 100 events in history | ~50KB |
|
||
|
|
|
||
|
|
## Constraints
|
||
|
|
|
||
|
|
✅ No external dependencies
|
||
|
|
✅ In-process only (no network/persistence)
|
||
|
|
✅ Type hints throughout
|
||
|
|
✅ Graceful error handling
|
||
|
|
✅ No global state beyond singleton
|
||
|
|
|
||
|
|
## Integration Points
|
||
|
|
|
||
|
|
### With Cognitive Kernel
|
||
|
|
|
||
|
|
- Kernel emits 4-5 events per execution
|
||
|
|
- Events contain result metadata
|
||
|
|
- Handler can introspect execution details
|
||
|
|
|
||
|
|
### With External Systems (Future)
|
||
|
|
|
||
|
|
- REST API wrapper (emit events → HTTP)
|
||
|
|
- Message queue bridge (emit events → Kafka/RabbitMQ)
|
||
|
|
- Logging integration (events → structured logs)
|
||
|
|
- Metrics export (events → Prometheus/CloudWatch)
|
||
|
|
|
||
|
|
## API Summary
|
||
|
|
|
||
|
|
| Function | Purpose |
|
||
|
|
|----------|---------|
|
||
|
|
| `get_event_bus()` | Singleton instance |
|
||
|
|
| `emit(type, payload)` | Publish event |
|
||
|
|
| `on(type, handler)` | Subscribe to events |
|
||
|
|
| `bus.publish(...)` | Direct publish |
|
||
|
|
| `bus.subscribe(...)` | Direct subscribe |
|
||
|
|
| `bus.unsubscribe(...)` | Remove handler |
|
||
|
|
| `bus.get_history(...)` | Query event history |
|
||
|
|
| `bus.clear_history()` | Reset history |
|
||
|
|
|
||
|
|
## Files
|
||
|
|
|
||
|
|
- **Implementation**: `glyphos/events.py` (175 lines)
|
||
|
|
- **Tests**: `tests/test_events.py` (520 lines)
|
||
|
|
- **Modified**: `glyphos/cognitive_kernel.py` (event emissions added)
|
||
|
|
|
||
|
|
## Status Summary
|
||
|
|
|
||
|
|
✅ **Implementation**: Complete
|
||
|
|
✅ **Testing**: 16/16 tests passing
|
||
|
|
✅ **Integration**: All 52 tests passing (36 + 16 new)
|
||
|
|
✅ **Documentation**: Complete
|
||
|
|
✅ **Backwards Compatibility**: Verified
|
||
|
|
|
||
|
|
**Ready for production deployment.**
|
||
|
|
|
||
|
|
## Future Enhancements
|
||
|
|
|
||
|
|
1. **Event Filtering**: Advanced queries on history
|
||
|
|
2. **Async Handlers**: Non-blocking event processing
|
||
|
|
3. **Event Replay**: Replay historical events for debugging
|
||
|
|
4. **Custom Events**: Allow applications to emit custom events
|
||
|
|
5. **Event Logging**: Write events to persistent log
|
||
|
|
6. **Metrics**: Built-in event rate/latency metrics
|
||
|
|
7. **Event Schema**: Validation of event payloads
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**The GlyphOS Event System is now live.** 🚀
|