100 lines
4.9 KiB
Markdown
100 lines
4.9 KiB
Markdown
# 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.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 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.rs` — `resolve_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. |