---
id: TASK-15
title: 'Add recursive config interpolation support for ${key} placeholders'
status: Done
assignee: []
created_date: '2026-02-19 15:47'
updated_date: '2026-02-19 17:03'
labels:
  - feature
  - config
dependencies: []
references:
  - lua/zync/lib/utils.lua
  - lua/zync/api/configurator.lua
  - lua/zync/api/configurator/sandbox.lua
  - lua/zync/api/manager.lua
  - spec/
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
Implement a mechanism to recursively interpolate all config fields using the config table itself as the key-value source for Utils.interpolate, enabling ${key} placeholders in args and other config values. Ensure interpolation respects active profile overrides so global args referencing ${board}, ${build_dir}, or ${project_dir} resolve based on the selected profile at runtime. Interpolation should handle nested tables (profiles, args) and avoid infinite loops or incorrect overwrites.
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Config values containing ${key} placeholders are recursively interpolated using the config table as the source map, including nested tables like profiles/args.
- [x] #2 Interpolation respects the active profile when resolving keys like board/build_dir so global args resolve appropriately per profile.
- [x] #3 Behavior is covered by unit tests, including examples for project_dir/build_dir interpolation and profile-specific board substitutions.
- [x] #4 Existing configs without placeholders continue to work unchanged.
<!-- AC:END -->

## Final Summary

<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Summary:
- Implemented recursive config interpolation and profile-aware placeholder resolution across config and profile tables.
- Added coverage for interpolation behavior and utils interpolation helpers to ensure placeholders resolve and untouched configs remain stable.

Tests:
- busted -c spec/api_spec/configurator_spec.lua
- busted -c spec/lib_spec/utils_spec.lua
- busted -c

Notes:
- No Definition of Done items were defined for this task.
<!-- SECTION:FINAL_SUMMARY:END -->
