Skip to content

feat: humanize_duration + default template helpers#18

Merged
zuchka merged 6 commits into
mainfrom
add-template-helpers
May 9, 2026
Merged

feat: humanize_duration + default template helpers#18
zuchka merged 6 commits into
mainfrom
add-template-helpers

Conversation

@zuchka
Copy link
Copy Markdown
Collaborator

@zuchka zuchka commented May 9, 2026

Summary

Inward-polish item 4 of 4 from the inward-polish roadmap. Adds two helpers to DING's message template engine and applies them across all recipes:

  • humanize_duration — renders numeric seconds via time.Duration.String() (e.g. 30m43s, 4m7.3s, 500ms)
  • default — returns fallback when value is nil or empty string (intentionally narrower than sprig — 0 and false pass through to avoid the {{ .exit_code | default 0 }} footgun)

Recipe sweep updates {{ .duration_seconds }}s{{ .duration_seconds | humanize_duration }} across all 9 platform recipes, _template.md, and ding.yaml.example. Documentation lands as a new ### Template helpers subsection in docs/configuration.md.

Backward compatibility

Empirically, Go's text/template renders missing fields as <no value> for both missingkey=default AND missingkey=zero when the data map type is map[string]interface{}. So even though we flipped to missingkey=zero to make default work for missing fields, the locked TestRenderMessage_MissingFieldStillRendersGracefully test still asserts the same output. Zero BC change for any existing recipe.

Commits

5 commits (planned 4; commit #4 is a follow-up prose fix caught by code-quality review):

  1. feat(evaluator): humanize_duration + default template helpers — pure helpers + 19 unit tests
  2. feat(evaluator): wire template helpers into renderMessage — engine.go FuncMap + missingkey=zero + 2 integration tests
  3. docs(recipes): use humanize_duration for duration_seconds — sweep across 9 recipes + template + ding.yaml.example
  4. docs(recipes): update rendered-output examples to match humanize_duration — modal.md "287s"→"4m47s" and ray.md "1843s"→"30m43s" prose fixes (caught by review)
  5. docs(configuration): document template helpers section — new ### Template helpers subsection

Test plan

  • go test ./... green
  • go test -race ./internal/evaluator/... green
  • go vet ./... green
  • ding test-rule end-to-end: humanize_duration renders 4m7.3s for duration_seconds=247.3
  • ding test-rule end-to-end: default renders unknown for missing branch label
  • ding validate accepts the pipe-form templates in all 9 recipes

🤖 Generated with Claude Code

zuchka and others added 6 commits May 8, 2026 18:31
Pure functions, not yet wired into renderMessage — that lands in the
next commit. Covered by 19 table-driven test cases across both helpers.

humanize_duration: any numeric seconds value through time.Duration.String()
default: returns fallback only on nil or empty string (narrower than
sprig — 0 and false pass through to avoid {{ .exit_code | default 0 }}
footgun)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds template.FuncMap registration for humanize_duration and default
plus Option("missingkey=zero") so missing fields surface via Go's zero
interface handling. Two new integration tests exercise both helpers
through the full renderMessage pipeline.

Note: missingkey=zero on map[string]interface{} still renders missing
keys as <no value> (nil interface prints that way in Go templates), so
TestRenderMessage_MissingFieldStillRendersGracefully required no update
— its existing <no value> assertion remains correct.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweeps the {{ .duration_seconds }}s pattern across all 9 platform
recipes, the canonical _template.md, and ding.yaml.example. Renders
human-readable durations (30m43s, 4m7.3s) instead of raw float seconds
(1843, 247.3) in the example alert messages.

Verified end-to-end via ding test-rule with a synthetic run.exit event.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tion

The "What you get" prose example in modal.md and ray.md showed raw
seconds (287s, 1843s) but the YAML now produces humanized output via
humanize_duration. Update prose to match: 287s -> 4m47s, 1843s -> 30m43s.

mlflow.md:90 was already consistent (42 humanizes to 42s, no change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a new ### Template helpers subsection covering humanize_duration
and default, with input/output tables matching the implementation
contract and a note on the deliberate diverge from sprig's default
semantics for 0/false values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The README's run.exit example still showed the raw {{ .duration_seconds }}s
form. Final-review caught the inconsistency: the recipe sweep updated all
9 platform recipes + _template.md + ding.yaml.example, but README.md
contains the canonical run.exit example a new user reads first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zuchka zuchka merged commit 6d2ef13 into main May 9, 2026
1 check passed
@github-actions github-actions Bot locked and limited conversation to collaborators May 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant