4.9 KiB
Work Summary: Template Resolver Refactor
Date: 2026-02-05
Overview
Moved the template resolver from the sensor crate to the common crate, renamed the template namespace from trigger.payload to event.payload, added event metadata fields, and wired the resolver into the executor's event processor for real template resolution during enforcement creation.
Motivation
- Wrong location: The template resolver lived in
crates/sensor/but template resolution happens in the executor when rules create enforcements. The sensor crate was the wrong home. - Misleading naming: Templates used
trigger.payload.*to reference data that actually comes from the event payload. The trigger defines the schema, but the runtime data is on the event. - Stub in executor: The executor's
event_processor.rshad aresolve_action_params()stub that just passed action_params through unresolved. Template resolution was never actually happening.
Changes
Template Resolver Moved to Common Crate
- New file:
crates/common/src/template_resolver.rs - Registered in:
crates/common/src/lib.rswith re-exports ofTemplateContextandresolve_templates - Sensor crate:
crates/sensor/src/lib.rsnow re-exports from common (pub use attune_common::template_resolver::*) for backward compatibility - Deleted:
crates/sensor/src/template_resolver.rs(old location)
Renamed trigger.payload → event.payload
The template namespace was changed from trigger to event across the entire codebase:
- Template syntax:
{{ trigger.payload.field }}→{{ event.payload.field }} - Struct field:
trigger_payload: JsonValue→event: JsonValue(restructured as a JSON object withpayload,id,trigger,createdsub-keys) - Context routing:
get_value()now routesevent.*paths with a skip count of 1 (instead oftriggerwith skip 2)
New Event Metadata in Templates
The event.* namespace now provides access to event metadata alongside the payload:
| Template | Description |
|---|---|
{{ event.payload.* }} |
Event payload fields (same data as before, just renamed) |
{{ event.id }} |
Event database ID (i64) |
{{ event.trigger }} |
Trigger ref that generated the event |
{{ event.created }} |
Event creation timestamp (RFC 3339) |
Builder methods on TemplateContext:
.with_event_id(id: i64).with_event_trigger(trigger_ref: &str).with_event_created(created: &str)
Executor Integration
crates/executor/src/event_processor.rs — resolve_action_params() was rewritten from a pass-through stub to a real implementation:
- Builds a
TemplateContextfrom theEventandRulemodels - Populates
event.id,event.trigger,event.created, andevent.payloadfrom the Event - Populates
system.timestamp,system.rule.id,system.rule.reffrom the Rule and current time - Pack config is currently passed as
{}(TODO: load from database) - Calls
resolve_templates()on the rule'saction_params
Documentation Updates
All documentation files updated from trigger.payload to event.payload:
docs/api/api-rules.mddocs/cli/cli.mddocs/examples/rule-parameter-examples.mddocs/guides/quickstart-example.mddocs/workflows/dynamic-parameter-forms.mddocs/workflows/parameter-mapping-status.md(also overhauled to reflect completed implementation)docs/workflows/rule-parameter-mapping.md(section headers and prose updated too)docs/workflows/rule-trigger-params.mdcrates/cli/README.md
Test and Script Updates
scripts/setup-test-rules.sh— updated template referencestests/e2e/tier3/test_t3_05_rule_criteria.pytests/e2e/tier3/test_t3_07_complex_workflows.pytests/e2e/tier3/test_t3_08_chained_webhooks.pytests/e2e/tier3/test_t3_16_rule_notifications.pytests/e2e/tier3/test_t3_17_container_runner.pytests/test_e2e_basic.py
Docker Fix (Separate Issue)
Fixed ARG NODE_VERSION scoping in Docker multi-stage builds:
docker/Dockerfile.sensor.optimized— addedARG NODE_VERSION=20insidesensor-fullstagedocker/Dockerfile.worker.optimized— addedARG NODE_VERSION=20insideworker-nodeandworker-fullstages
Global ARGs are only available in FROM instructions; they must be re-declared inside stages to use in RUN commands.
Test Results
- 20 unit tests in
attune_common::template_resolver— all pass - 78 executor lib tests — all pass
- Sensor tests — all pass (re-export works correctly)
- Zero compiler warnings across workspace
Remaining Work
- Pack config loading: The executor currently passes
{}for pack config context.{{ pack.config.* }}templates will resolve to null until pack config is loaded from the database. - Work summaries left as-is: Historical work summaries in
work-summary/still reference the oldtrigger.payloadsyntax. These are historical records and were intentionally not updated.