Skip to content

fix(config): keep an explicit false/0/"" from config.yaml - #632

Merged
EricAndrechek merged 2 commits into
mainfrom
fix/config-yaml-zero
Sep 25, 2026
Merged

EricAndrechek merged 2 commits into
mainfrom
fix/config-yaml-zero

Conversation

@EricAndrechek

@EricAndrechek EricAndrechek commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Fixes #631.

What was wrong

Each boot-config default lived in a cleanenv env-default tag. cleanenv applies those after the YAML decode, to any field that is still at its zero value. So it could not tell a key the file set to false/0/"" apart from a key the file left out. otel.traces.enabled: false loaded as true, and sample_rate: 0 loaded as 1.0. Nothing reported it.

The fix (direction 1 from the issue)

  • internal/config/config.go: a new unexported defaults() is now the only place defaults are defined. Load starts from it, cleanenv decodes the YAML over it, and then applies the WH_* variables. Precedence is still env > YAML > default. All env-default tags are gone. The env tags are unchanged, so unboundEnv, rejectUnknownKeys and TestEnvSettingsDir_MatchesStructTag behave exactly as before.
  • I did not choose direction 2 (record which keys the file set, then restore them after cleanenv). It would keep the defaults in tags and add a second pass that has to stay in step with cleanenv. Direction 1 removes the cause and is smaller.

Behaviour change

A zero value you write in config.yaml now takes effect. If your file relied on the bug:

  • sample_rate: 0 now exports no traces, or no DEBUG/INFO logs. Before, it silently exported everything.
  • A signal set to enabled: false is now off.
  • shutdown_timeout: 0 now skips the drain.
  • cache.l1_max_cost: 0 now refuses boot with cache init: MaxCost can't be zero. server.port: 0 and data_dir: "" also refuse boot. An empty prometheus.path refuses boot when Prometheus is enabled.

To get the default back, delete the key. Env vars already honoured an explicit zero, so they are unchanged. This is described under Fixed in the CHANGELOG.

Tests (internal/config/defaults_test.go, all through config.Load)

  • TestLoad_YAMLZeroIsKept: for each key in the issue's table, a YAML false/0/"" comes back unchanged. This is the case that fails if defaults are applied again after the decode. I confirmed it: with the old loader it fails, and so do TestLoad_IssueReproFile, TestLoad_YAMLZeroPortIsRefused and TestConfig_NoEnvDefaultTags.
  • TestLoad_IssueReproFile: loads the issue's repro file as written.
  • TestLoad_AbsentKeyGetsDefault: when the file exists but leaves a key out, that key gets its default.
  • TestLoad_EnvWinsOverYAMLZeroAndDefault: env wins over a YAML zero, an env zero wins over a YAML value, and an env zero wins over the default when there is no file.
  • TestZeroCases_CoverEveryNonZeroDefault: a new non-zero default without regression coverage fails this test.
  • TestConfig_NoEnvDefaultTags: refuses any env-default tag.
  • TestDocs_DefaultsMatchCode: each Config field has exactly one row in configuration.mdx. The row must name the field's env var, and its Default column must parse to the value in defaults(). Doc rows with no matching field also fail.

Deliberately left out

Reviewers

  • pre-push-reviewer (opus), at 252ef7b: ship_it, with 0 MUST, 0 SHOULD and 0 MAY findings. It read cleanenv v1.5.0's source to confirm the precedence. It also checked that no shipped config file (config.yaml, tests/e2e/fixtures/config.yaml, deployments/compose/standalone.yaml) relied on the bug.
  • docs-reviewer (opus), at 252ef7b: ship_it, with 0 findings.

Gate gap #454: reviewer markers land in the main checkout, not this worktree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EJr5tY4WQUy2sc4MbW67vL

cleanenv applies env-default after the YAML decode to any field still at
its zero value, so an explicit `otel.traces.enabled: false` or
`sample_rate: 0` came back as the default. Defaults now live in one Go
function that Load starts from before the decode; env-default is gone.
Tests load through config.Load for every affected key, refuse an
env-default tag, and pin configuration.mdx's defaults to defaults().

Fixes #631.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJr5tY4WQUy2sc4MbW67vL
@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 255328ad-972a-48eb-8dde-623fe3916a07

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added documentation Improvements or additions to documentation go Pull requests that update go code area/docs Documentation, site/, README labels Sep 25, 2026
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

📚 Docs preview is live → https://6e1fc77c-wavehouse-docs.wave-rf.workers.dev

  • Commit — 40b5038: Merge remote-tracking branch 'origin/main' into fix/config-yaml-zero
  • Author — @EricAndrechek
  • Committed — 2026-09-25 18:30 (UTC-04:00)
  • Deployed — 2026-09-25 18:39 EDT

@github-code-quality

github-code-quality Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go

The overall line coverage in commit 40b5038 in the fix/config-yaml-zero branch is 94%. The line coverage in commit 0cbb386 in the main branch is 93%.

Show a line coverage summary of the most impacted files.
File main 0cbb386 fix/config-yaml-zero 40b5038 +/-
internal/mq/embedded.go 88% 89% +1%
internal/ingest/worker.go 97% 98% +1%
internal/config/config.go 94% 97% +3%

Updated September 25, 2026 22:39 UTC

@EricAndrechek
EricAndrechek marked this pull request as ready for review September 25, 2026 22:34
@EricAndrechek
EricAndrechek requested review from a team and taitelee September 25, 2026 22:34
@EricAndrechek
EricAndrechek merged commit cb78f78 into main Sep 25, 2026
22 of 32 checks passed
@EricAndrechek
EricAndrechek deleted the fix/config-yaml-zero branch September 25, 2026 22:43
EricAndrechek added a commit that referenced this pull request Sep 26, 2026
Brings in #647, #632, #616 and #655. One conflict, in config.go: main
dropped the Cache struct's env-default tag while this branch moved Cache
into backends.go; kept the move.

#632 removed every env-default tag. The four *.backend fields and
cache.l1_max_cost this branch declares in backends.go still carried
theirs, so an explicit `backend: ""` would have become the default. Their
defaults now live in defaults(); an empty backend, which Validate refuses,
is pinned next to server.port's zero as a refusal; the docs test converts
a documented default to a named string type such as MQBackend.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
EricAndrechek added a commit that referenced this pull request Sep 26, 2026
Brings in #618 (squash-merged parent), #619, #632, #616, #655 and #647.
Conflicts resolved by keeping main's content plus this branch's coord
changes; the AGENTS.md package count is now twenty (keyenc + coord).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
EricAndrechek added a commit that referenced this pull request Sep 26, 2026
Brings in main via feat/coord-leases: #618's squash, #619, #632, #616,
#655 and #647. Per #632, the roles default moves from its env-default tag
into defaults(): an explicit `roles: []` now reaches Validate (a
refusedZeros entry pins it), and the doc-defaults test parses the roles
cell as a comma-separated list. instance_id's documented default is
*(empty)*, the value in defaults(); Load resolves it to
<hostname>-<8 hex>.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
EricAndrechek added a commit that referenced this pull request Sep 26, 2026
main now carries #612 and #618 as squashes, plus #632, #622, #627,
#615, #619, #647, #616, #655 and #623. The merge was resolved against the
pre-squash #618 head (f129d57) as its base, so main's version wins for
everything this stack does not own and only the cache stack's changes
(#614, #621, #626 as merged here, and this PR) are re-applied on top.

Warnings keeps main's api-role gate for the cache.redis warnings too: a
split's Deployments differ only in roles, so the API's cover the others'.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017aS7rLrH1RKkUMem7X4ckd
EricAndrechek added a commit that referenced this pull request Sep 26, 2026
Adopt #632's convention for the cache.redis block: its seven non-zero
defaults move from env-default tags into defaults(), each gets a zero
case, and the configuration.mdx rows use *(empty)* for an empty default.
The doc-defaults test learns to read a duration and an empty list.

With the file's zeros now kept, compress_min_bytes needs no -1: 0 means
never compress, the backend's own meaning, from the file and the
environment alike, and wire.go passes it through unchanged. A negative
value refuses boot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017aS7rLrH1RKkUMem7X4ckd
EricAndrechek added a commit that referenced this pull request Sep 29, 2026
Since #632 an env-default tag is refused: cleanenv re-applies it to a
YAML zero, so `mq.nats.partitions: 0` came back 1. The block's defaults
now come from defaultMQNATS() inside defaults(), each non-zero one has
a zeroCases entry (the block is validated only under backend=nats, so
its zeros load as written), the docs test reads a derived default such
as `<prefix>_coord` and *(none)* as the zero, and the TLS pair gets a
row per key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FyrXjhR7iDg33paioLQHFq
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation, site/, README documentation Improvements or additions to documentation go Pull requests that update go code

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

bug(config): an explicit false/0 in config.yaml is replaced by the field's env-default

1 participant