12 KiB
Work Summary: Rule Action Parameters Implementation
Date: 2026-01-16
Session Focus: Implementing action parameter passing from rules to executions
Objective
Implement the ability for rules to specify action parameters that get passed through the execution pipeline. This enables rules to configure what parameters actions receive when triggered, which is essential for the "hello, world" timer echo demo.
Problem Statement
The rule system lacked a way to specify parameters for actions. When a timer triggered a rule that should execute core.echo with a message, there was no mechanism to pass the message parameter. The flow was:
Event → Rule Match → Enforcement → Execution
But the execution had no way to know what parameters to pass to the action (e.g., {"message": "hello, world"}).
Solution Implemented
Added an action_params JSONB field to rules that stores the parameters to pass to actions when the rule is triggered. These parameters flow through the pipeline:
Rule (action_params) → Enforcement (config) → Execution (config) → Worker (action input)
Changes Made
1. Database Schema
Migration: migrations/20240103000003_add_rule_action_params.sql
- Added
action_params JSONB DEFAULT '{}'::jsonbcolumn toattune.ruletable - Created GIN index
idx_rule_action_params_ginfor efficient JSON querying - Added column comment documenting the field's purpose
2. Data Models
File: crates/common/src/models.rs
- Added
pub action_params: JsonValuefield toRulestruct
3. Repository Layer
File: crates/common/src/repositories/rule.rs
Updated all rule-related operations:
- Added
action_paramstoCreateRuleInputstruct - Added
action_paramstoUpdateRuleInputstruct (asOption<JsonValue>) - Updated all
SELECTqueries to includeaction_paramscolumn - Updated
INSERTquery increate()to includeaction_params - Updated
UPDATEquery builder to handleaction_paramsupdates - Updated all specialized queries (
find_by_pack,find_by_action,find_by_trigger,find_enabled)
4. API Layer
File: crates/api/src/dto/rule.rs
- Added
action_paramsfield toCreateRuleRequestwith default{} - Added
action_paramsfield toUpdateRuleRequestas optional - Added
action_paramsfield toRuleResponse - Updated all test fixtures to include
action_params
File: crates/api/src/routes/rules.rs
- Updated
create_rulehandler to includeaction_paramsinCreateRuleInput - Updated
update_rulehandler to includeaction_paramsinUpdateRuleInput - Updated
enable_ruleanddisable_rulehandlers to setaction_params: None
5. Enforcement Creation
File: crates/sensor/src/rule_matcher.rs
- Updated
create_enforcementto copyrule.action_paramstoenforcement.config - Updated
find_matching_rulesquery to includeaction_paramscolumn - This ensures parameters flow from rule to enforcement
6. Sensor Manager Fix
File: crates/sensor/src/sensor_manager.rs
Fixed an issue where built-in timer sensors were being incorrectly processed:
- Added
load_runtime()method to fetch runtime information - Added logic in
start()to skip sensors withcore.sensor.builtinruntime - These sensors are managed directly by the timer service, not through sensor execution
- Added
use anyhow::{anyhow, Result}anduse attune_common::models::Runtime
7. Database Updates
Updated the existing test rule with action parameters:
UPDATE attune.rule
SET action_params = '{"message": "hello, world"}'::jsonb
WHERE ref = 'core.timer_echo_10s';
8. Testing Infrastructure
File: scripts/test_timer_echo.sh
Created a test script that:
- Verifies database setup (rule with action_params exists)
- Starts sensor, executor, and worker services
- Monitors logs for "hello, world" output
- Provides color-coded log highlighting
- Includes cleanup on exit
Data Flow
The complete flow for action parameter passing:
- Rule Definition - Rule stores
action_params: {"message": "hello, world"} - Event Matching - When an event matches the rule's trigger
- Enforcement Creation -
rule_matchercreates enforcement withconfig = rule.action_params - Execution Creation -
executorcreates execution withconfig = enforcement.config - Action Execution -
workerpassesexecution.configto the action as input parameters
Verification
Database Schema Verification
-- Check column exists
\d attune.rule
-- Check index exists
\di attune.idx_rule_action_params_gin
-- Verify data
SELECT ref, action_ref, action_params::text
FROM attune.rule
WHERE ref = 'core.timer_echo_10s';
Expected output:
ref | action_ref | action_params
---------------------+------------+-----------------------------
core.timer_echo_10s | core.echo | {"message": "hello, world"}
Code Compilation
All services compile successfully:
attune-api- ✅attune-executor- ✅attune-worker- ✅attune-sensor- ✅
SQLx Query Cache
Ran cargo sqlx prepare --workspace successfully to update query cache with new schema.
Known Issues
Timer Event Generation - RESOLVED ✅
Root Cause Identified:
The timer manager was using trigger.id as the HashMap key for storing timer instances. Multiple sensors (e.g., core.timer_10s_sensor and core.timer_1m_sensor) shared the same trigger (core.intervaltimer, ID 15). When starting the second timer, it would stop and overwrite the first timer in the HashMap.
Solution Implemented:
Changed the timer manager to key by (trigger_id, config_hash) instead of just trigger_id. This allows multiple sensors with the same trigger but different configurations to coexist.
Changes Made:
- Added
timer_key()function that generates unique key:format!("{}:{}", trigger_id, config_hash) - Changed
TimerManagerInner.timersfromHashMap<i64, TimerInstance>toHashMap<String, TimerInstance> - Updated all timer operations to use the new key format
Verification:
- Timer tasks now fire successfully every 10 seconds
- Events are created in database (verified 6+ events)
- Enforcements are created with correct
action_paramsin config - "Interval timer fired (iteration N)" messages appear in logs
Worker Runtime Matching Issue
Current Blocker:
The executor cannot find compatible workers for core.echo action because:
- Action requires
core.action.shellruntime (ID 3) - Worker has
runtime = NULLin database - Executor filters by exact runtime ID match:
w.runtime == Some(runtime.id)
Temporary Workaround Applied:
UPDATE attune.worker SET runtime = 3 WHERE name = 'worker-hp-probook-cachy';
Proper Solution Needed:
- Worker capabilities include
"runtimes": ["python", "shell", "node"] - Executor should match workers based on capabilities, not runtime column
- Worker schema allows only one runtime, but workers can support multiple
- Need to refactor executor's
select_worker()to check capabilities field
Workaround
End-to-End Flow Status:
- ✅ Timer fires every 10 seconds
- ✅ Events created in database
- ✅ Rules matched successfully
- ✅ Enforcements created with
{"message": "hello, world"}in config - ⚠️ Executions created but not scheduled to workers (runtime matching issue)
- ❌ Worker not receiving execution messages yet
The pipeline works up to enforcement creation. The blocker is worker runtime matching in the executor.
Files Modified
migrations/20240103000003_add_rule_action_params.sql- NEWcrates/common/src/models.rs- Modified Rule structcrates/common/src/repositories/rule.rs- Added action_params to all operationscrates/api/src/dto/rule.rs- Added action_params to DTOscrates/api/src/routes/rules.rs- Updated handlerscrates/sensor/src/rule_matcher.rs- Copy action_params to enforcementcrates/sensor/src/sensor_manager.rs- Skip built-in sensors, added load_runtime methodcrates/sensor/src/timer_manager.rs- Fixed timer key collision, added extensive debug loggingscripts/test_timer_echo.sh- NEW test script.sqlx/query-*.json- Updated SQLx query cache files
Documentation Impact
The following documentation should be updated:
docs/api-rules.md- Addaction_paramsfield documentationdocs/quickstart-timer-demo.md- Update withaction_paramsin examples- API OpenAPI spec - Include
action_paramsin rule schemas
Next Session Tasks
-
FIX WORKER RUNTIME MATCHING - Priority: CRITICAL ✅ TIMER ISSUE RESOLVED
- Refactor
ExecutionScheduler::select_worker()to use capabilities - Parse
capabilities.runtimesarray instead ofworker.runtimecolumn - Test with multiple runtime types (shell, python, node)
- Consider deprecating
worker.runtimecolumn in favor of capabilities
- Refactor
-
COMPLETE END-TO-END TESTING - Priority: HIGH
- Verify complete flow: sensor → event → enforcement → execution → worker
- Confirm "hello, world" appears in worker stdout/logs
- Test with different action parameters
- Verify parameter flow through entire pipeline
-
Documentation Updates
- Update API documentation with
action_params - Add examples showing parameter passing
- Document the complete data flow
- Update API documentation with
-
Additional Testing
- Test parameter templating (future: use event payload in params)
- Test with different action types
- Test parameter validation
Architectural Notes
Design Decision: Rule-Level vs Event-Level Parameters
We chose to store action parameters at the rule level rather than computing them dynamically from events. This provides:
Pros:
- Simple and predictable
- Works for all use cases
- Easy to understand and debug
- Database stores complete execution configuration
Cons:
- Less flexible (can't use event data in parameters yet)
- Static parameters only
Future Enhancement: Add parameter templating to allow rules to construct parameters from event payload, similar to StackStorm's Jinja2 templating.
Parameter Flow Architecture
The three-stage parameter flow (rule → enforcement → execution) provides:
- Isolation - Each stage can operate independently
- Audit Trail - Complete history of what parameters were used
- Replay - Can re-execute with same parameters
- Override - Future: Allow execution-time parameter overrides
Success Criteria
- Database schema supports action_params
- API accepts and returns action_params
- Rule repository handles action_params
- Enforcement copies action_params to config
- Built-in sensors skip sensor execution
- Timer events fire successfully ✅ RESOLVED
- Timer key collision bug fixed ✅ RESOLVED
- Worker runtime matching works (IN PROGRESS)
- End-to-end flow completes to worker execution (BLOCKED by runtime matching)
Conclusion
Major Accomplishment: Both the action parameter infrastructure AND the timer firing issue have been resolved!
The data flow now works correctly through the entire pipeline:
- ✅ Timer fires every 10 seconds (fixed HashMap key collision)
- ✅ Events are created in database
- ✅ Rules are matched successfully
- ✅ Enforcements are created with
{"message": "hello, world"}in config - ✅ Executions are created with correct parameters
- ⚠️ Final blocker: Executor cannot find compatible workers (runtime matching logic)
Remaining Work:
The only remaining issue is the worker runtime matching in the executor. Once the select_worker() function is updated to check worker capabilities instead of the runtime column, the complete happy path will work end-to-end and "hello, world" will appear in the worker logs.
Timer Fix Details: The timer collision bug was subtle but critical. Multiple sensors using the same trigger would overwrite each other in the HashMap. The fix generates a unique key combining trigger ID and config hash, allowing multiple timer instances per trigger type.