Files
attune/work-summary/2026-02-05-template-resolver-refactor.md

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

  1. 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.
  2. 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.
  3. Stub in executor: The executor's event_processor.rs had a resolve_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.rs with re-exports of TemplateContext and resolve_templates
  • Sensor crate: crates/sensor/src/lib.rs now re-exports from common (pub use attune_common::template_resolver::*) for backward compatibility
  • Deleted: crates/sensor/src/template_resolver.rs (old location)

Renamed trigger.payloadevent.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: JsonValueevent: JsonValue (restructured as a JSON object with payload, id, trigger, created sub-keys)
  • Context routing: get_value() now routes event.* paths with a skip count of 1 (instead of trigger with 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.rsresolve_action_params() was rewritten from a pass-through stub to a real implementation:

  • Builds a TemplateContext from the Event and Rule models
  • Populates event.id, event.trigger, event.created, and event.payload from the Event
  • Populates system.timestamp, system.rule.id, system.rule.ref from the Rule and current time
  • Pack config is currently passed as {} (TODO: load from database)
  • Calls resolve_templates() on the rule's action_params

Documentation Updates

All documentation files updated from trigger.payload to event.payload:

  • docs/api/api-rules.md
  • docs/cli/cli.md
  • docs/examples/rule-parameter-examples.md
  • docs/guides/quickstart-example.md
  • docs/workflows/dynamic-parameter-forms.md
  • docs/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.md
  • crates/cli/README.md

Test and Script Updates

  • scripts/setup-test-rules.sh — updated template references
  • tests/e2e/tier3/test_t3_05_rule_criteria.py
  • tests/e2e/tier3/test_t3_07_complex_workflows.py
  • tests/e2e/tier3/test_t3_08_chained_webhooks.py
  • tests/e2e/tier3/test_t3_16_rule_notifications.py
  • tests/e2e/tier3/test_t3_17_container_runner.py
  • tests/test_e2e_basic.py

Docker Fix (Separate Issue)

Fixed ARG NODE_VERSION scoping in Docker multi-stage builds:

  • docker/Dockerfile.sensor.optimized — added ARG NODE_VERSION=20 inside sensor-full stage
  • docker/Dockerfile.worker.optimized — added ARG NODE_VERSION=20 inside worker-node and worker-full stages

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 old trigger.payload syntax. These are historical records and were intentionally not updated.