Overview
Pulse emits events at every layer, making it possible to observe signal flow, debug unexpected behavior, and understand why (or why not) an escalation occurred.The Event Flow
Signals flow through three layers:The
SignalBus (pulse/bus.py) is an internal event dispatcher that broadcasts SignalEvent objects from the Retina:from pulse import PulseRegistry
pulse = PulseRegistry(watch_dirs=[])
# Define a callback
def on_signal(event):
print(f"[RETINA] {event.source} | {event.delta_type} | {event.location}")
# Subscribe before starting
pulse._bus.subscribe(on_signal)
pulse.start()
The
_bus attribute is internal (prefixed with _). Direct access is not part of the public API and may change. For production use, prefer subscribing to Layer 3 escalations via on_escalation().@dataclass
class SignalEvent:
source: str # "filesystem" | "memory" | "time" | "network"
location: str # path, namespace, or endpoint
delta_type: str # "created" | "modified" | "deleted" | "tick"
magnitude: float # 0.0–1.0, normalized change size
timestamp: float # unix timestamp
features: dict # source-specific features
{
"path": "/home/user/Downloads/hw3.pdf",
"extension": ".pdf",
"size_bytes": 204800,
"directory_depth": 3,
"filename_tokens": ["hw3"]
}
{
"hour_sin": 0.866, # sin(2π * hour / 24)
"hour_cos": 0.5,
"dow_sin": 0.782, # sin(2π * day_of_week / 7)
"dow_cos": 0.623,
"minutes_since_last_activation": 847
}
from pulse import PulseRegistry, EscalationDecision
pulse = PulseRegistry(watch_dirs=[])
def on_escalation(decision: EscalationDecision):
print(f"[ESCALATION] Module: {decision.module_id}")
print(f" Question: {decision.question}")
print(f" Confidence: {decision.confidence:.2f}")
print(f" Events: {len(decision.triggering_events)}")
pulse.on_escalation(on_escalation)
pulse.start()
@dataclass
class EscalationDecision:
module_id: str
question: str # Interpolated from template
confidence: float # Layer 2 relevance score
triggering_events: list[SignalEvent]
should_escalate: bool # True if threshold exceeded
def on_escalation(decision: EscalationDecision):
print(f"\n[ESCALATION] {decision.module_id}")
print(f"Question: {decision.question}")
print(f"Confidence: {decision.confidence:.2f}\n")
for i, event in enumerate(decision.triggering_events, 1):
print(f" Event {i}:")
print(f" Source: {event.source}")
print(f" Type: {event.delta_type}")
print(f" Location: {event.location}")
print(f" Magnitude: {event.magnitude:.2f}")
print(f" Features: {event.features}")
Debugging: Why Didn’t My Module Escalate?
If you expect an escalation but don’t see one, check each layer:Layer 1: Is the Retina detecting the event?
Layer 1: Is the Retina detecting the event?
Check:If you see the event logged, Layer 1 is working. Move to Layer 2.
- Is the directory in your fingerprint’s
watch_directories? - Is the Retina running? Call
pulse.start()before making changes.
Layer 2: Is the relevance score above threshold?
Layer 2: Is the relevance score above threshold?
Check:If the score is too low:
- What is the module’s
default_threshold? (default: 0.65) - Has the model been trained on similar events?
- The model hasn’t learned this pattern yet (provide training feedback)
- The fingerprint’s signal priors don’t match the event (update the fingerprint)
Layer 3: Is the question template valid?
Layer 3: Is the question template valid?
Check:If
- Does the template contain
{location}? - Are there any exceptions in the prefrontal layer?
should_escalate field:should_escalate=False, the score was below threshold.Advanced Monitoring: Feature Vectors
To see exactly what Layer 2 receives, convert events to feature vectors:| Index | Feature | Description |
|---|---|---|
| 0 | magnitude | Event magnitude (0.0–1.0) |
| 1 | delta_type | Encoded as 0.25/0.5/0.75/1.0 |
| 2 | source | Encoded as 0.25/0.5/0.75/1.0 |
| 3 | hour_sin | sin(2π × hour / 24) |
| 4 | hour_cos | cos(2π × hour / 24) |
| 5 | dow_sin | sin(2π × day / 7) |
| 6 | dow_cos | cos(2π × day / 7) |
| 7 | minutes_since_last | Time since last activation |
| 8 | size_bytes | log₁₀(size) normalized |
| 9 | directory_depth | Path depth normalized |
| 10 | extension_hash | CRC32 hash % 1000 / 1000.0 |
| 11–15 | reserved | For memory/network features |
pulse/fingerprint.py:17-30 for the complete layout.
Performance Monitoring
Track Pulse’s resource usage:retina-forwarder: Forwards events from Retina to SignalBuspulse-scorer-X: Thread pool workers (4 by default) for LSTM inference- Watchdog observer threads (one per monitored directory)
- Idle: ~0% (Retina only wakes on filesystem events or time tick)
- Active: < 5% (LSTM inference is fast on CPU)
Logging Signal Flow
For debugging, log the complete flow:Troubleshooting
No events are being logged
No events are being logged
Check:
- Did you call
pulse.start()? - Are watch directories correct? Print them:
- Are you making changes in monitored directories?
Too many events (noise)
Too many events (noise)
Reduce noise by:
- Narrowing
watch_directoriesin fingerprints - Adding more
irrelevant_extensions - Increasing
default_threshold(fewer false positives)
Events detected but no escalations
Events detected but no escalations
The model’s relevance score is below threshold. Either:
- Lower the
default_threshold(temporary fix) - Provide training labels to teach the model (proper fix)
Next Steps
Training Models
Learn how to improve model accuracy with feedback
Integration
Integrate Pulse into your agent system