feat: humanize_duration + default template helpers#18
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 viatime.Duration.String()(e.g.30m43s,4m7.3s,500ms)default— returns fallback when value is nil or empty string (intentionally narrower than sprig —0andfalsepass 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, andding.yaml.example. Documentation lands as a new### Template helperssubsection indocs/configuration.md.Backward compatibility
Empirically, Go's
text/templaterenders missing fields as<no value>for bothmissingkey=defaultANDmissingkey=zerowhen the data map type ismap[string]interface{}. So even though we flipped tomissingkey=zeroto makedefaultwork for missing fields, the lockedTestRenderMessage_MissingFieldStillRendersGracefullytest 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):
feat(evaluator): humanize_duration + default template helpers— pure helpers + 19 unit testsfeat(evaluator): wire template helpers into renderMessage— engine.go FuncMap + missingkey=zero + 2 integration testsdocs(recipes): use humanize_duration for duration_seconds— sweep across 9 recipes + template + ding.yaml.exampledocs(recipes): update rendered-output examples to match humanize_duration— modal.md "287s"→"4m47s" and ray.md "1843s"→"30m43s" prose fixes (caught by review)docs(configuration): document template helpers section— new ### Template helpers subsectionTest plan
go test ./...greengo test -race ./internal/evaluator/...greengo vet ./...greending test-ruleend-to-end:humanize_durationrenders4m7.3sforduration_seconds=247.3ding test-ruleend-to-end:defaultrendersunknownfor missingbranchlabelding validateaccepts the pipe-form templates in all 9 recipes🤖 Generated with Claude Code