Skip to content

feat: support {{ var | default xxx }} fallbacks in template fields#42

Merged
rhoninl merged 1 commit into
mainfrom
feat/template-default-values
May 22, 2026
Merged

feat: support {{ var | default xxx }} fallbacks in template fields#42
rhoninl merged 1 commit into
mainfrom
feat/template-default-values

Conversation

@rhoninl

@rhoninl rhoninl commented May 22, 2026

Copy link
Copy Markdown
Owner

Summary

Templated fields can now carry a per-variable fallback: {{ key.path | default text }}. When the key resolves to null/undefined, the literal text is substituted instead of an empty string. Each placeholder gets its own independent default.

Behavior

  • Missing → default. {{ a.b | default fallback }} resolves to fallback when a.b is null/undefined.
  • No warning when a default is present. Since the author explicitly handled absence, no template_warning is recorded — and a defaulted ref on a critical config field (e.g. agent prompt) no longer fails the node onto its error branch.
  • null/undefined only count as missing. An explicit empty-string value is a real value and is not replaced by the default — consistent with the existing warning condition.
  • Default value is literal text, trimmed of outer whitespace, may contain internal spaces, cannot contain }. default must be followed by whitespace.

Changes

  • lib/shared/template-refs.tsTEMPLATE_REF_RE extended to capture an optional | default … group; it is now the single shared matcher, imported by the runtime resolver so the lint pass and resolver can't drift. lintField suppresses the "may legitimately be absent" warnings (out-of-scope / missing-global / missing-input) when a default is present, but still flags genuine typos (unknown / missing-field / self-ref) via STRUCTURAL_LINT_REASONS.
  • lib/server/templating.tsresolve() substitutes the default on a missing key and emits no warning when a default is present.
  • app/components/TemplateField.tsxfindTemplateSlot clamps the autocomplete slot to the ref name so re-picking a ref preserves an existing | default clause, and stays dormant for an unclosed placeholder where an inserted }} would have nowhere correct to go.
  • 23 new test cases across the two *.test.ts files.

Testing

  • Full unit suite: 1274 pass / 0 fail.
  • Typecheck clean for all changed files.
  • Direct functional check of resolve() confirms each scenario (missing→default, present→real value, per-variable defaults, empty default, no-default warning unchanged).

Review

  • Design reviewed (tech-lead) — expanded scope to all {{...}} consumers; adopted the narrower lint rule.
  • Code reviewed (senior) — caught and fixed a bug where an unclosed {{ ref | default … had no valid spot for an inserted closer.

🤖 Generated with Claude Code

A templated field can now carry a per-variable fallback:
`{{ key.path | default text }}`. When the key resolves to null /
undefined the literal `text` is substituted, and — because the author
explicitly handled absence — no template warning is recorded. This also
means a defaulted ref on a critical config field no longer fails the
node onto its error branch. An explicit empty-string value is a real
value and is not replaced by the default.

- Consolidate the `{{ … }}` matcher into one shared `TEMPLATE_REF_RE`
  (lib/shared/template-refs.ts), imported by the runtime resolver, so the
  lint pass and the resolver can never drift.
- lintField suppresses the "may legitimately be absent" warnings
  (out-of-scope / missing-global / missing-input) when a default is
  present, but still flags genuine typos (unknown / missing-field /
  self-ref) — STRUCTURAL_LINT_REASONS.
- findTemplateSlot clamps the autocomplete slot to the ref name so
  re-picking a ref preserves an existing `| default` clause, and stays
  dormant for an unclosed placeholder where an inserted closer would
  have nowhere correct to go.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rhoninl rhoninl merged commit 52ccae9 into main May 22, 2026
2 checks passed
@rhoninl rhoninl deleted the feat/template-default-values branch May 22, 2026 10:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant