Files
attune/work-summary/2026-02-04-timer-sensor-discriminator-fix.md
2026-02-04 17:46:30 -06:00

4.2 KiB

Timer Sensor Discriminator Fix

Date: 2026-02-04
Status: Fixed
Type: Bug Fix

Problem

The timer sensor was failing to start timers with the error:

Failed to start timer for rule 1: Failed to parse trigger_params as TimerConfig

Root Cause

The timer sensor's TimerConfig enum is defined as a tagged union using serde's #[serde(tag = "type")]:

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TimerConfig {
    Interval { interval: u64, unit: TimeUnit },
    Cron { expression: String },
    DateTime { fire_at: DateTime<Utc> },
}

This requires JSON with a type discriminator field:

{
  "type": "interval",
  "interval": 1,
  "unit": "seconds"
}

However, the trigger_params stored in the rule table was missing the type field:

{
  "interval": 1,
  "unit": "seconds"
}

Without the discriminator, serde couldn't deserialize the enum variant, causing the parse error.

Investigation Steps

  1. Observed error in sensor logs
  2. Checked database for trigger_params content
  3. Found parameters were correct but missing type field
  4. Examined TimerConfig enum definition in crates/sensor-timer/src/types.rs
  5. Identified the tagged union requirement

Solution

Immediate Fix: Manually added the type field to existing rule:

UPDATE rule 
SET trigger_params = jsonb_set(trigger_params, '{type}', '"interval"') 
WHERE trigger_ref = 'core.intervaltimer';

Result: Timer sensor now working correctly, firing events every second.

This issue was NOT caused by the JSON Schema format migration. This is a separate data format issue:

  • Schema format: How parameter definitions are structured (inline vs standard JSON Schema)
  • Data format: How parameter values are stored (with or without type discriminator)

The JSON Schema migration only affected the parameter schema definitions in YAML files and the database. It did not affect the parameter values stored in rules.

Proper Long-Term Fix

The issue should be fixed at the source - when rules are created. The system should automatically add the appropriate type discriminator to trigger_params based on the trigger_ref:

Trigger Ref Type Value
core.intervaltimer "interval"
core.crontimer "cron"
core.datetimetimer "datetime"

Implementation Options

  1. Database Trigger: Add a PostgreSQL trigger to automatically inject the type field
  2. API Layer: Add the type field in the API when creating/updating rules
  3. Repository Layer: Add the type field in the rule repository's create/update methods
  4. Timer Sensor: Make the timer sensor more flexible to infer type from trigger_ref

Recommended: Option 3 (Repository Layer) - keep the logic in Rust code where it's testable and maintainable.

Files Involved

  • crates/sensor-timer/src/types.rs - Defines TimerConfig enum with tagged union
  • crates/sensor-timer/src/timer_manager.rs - Deserializes TimerConfig from rule params
  • Database: rule table, trigger_params column

Verification

After applying the fix:

# Check rule parameters
docker compose exec -T postgres psql -U attune -d attune \
  -c "SELECT id, ref, jsonb_pretty(trigger_params) FROM rule;"

# Verify timer is working
docker compose logs sensor | grep "Timer fired"

Expected output:

  • Rule parameters include "type": "interval"
  • Sensor logs show "Timer fired for rule 1, created event X" every second
  • Events are being created in the database

Action Items

  • Implement automatic type field injection in rule repository
  • Add validation to ensure timer trigger params include required type field
  • Update rule creation tests to verify type field is present
  • Consider making timer sensor more lenient (infer type from trigger_ref)
  • Document the required format for timer trigger parameters

Conclusion

The timer sensor is now operational. The issue was a missing discriminator field in the stored parameter data, not a problem with the JSON Schema migration. The system needs a proper fix to automatically add the type field when creating rules with timer triggers.