feat: give feature switches the config.go treatment #925

Merged
coilysiren merged 1 commit from issue-854-feature-flag-table into main 2026-08-17 19:22:23 +00:00
Member

The numbers have one file, one helper, a validator, and a generated reference. The switches now have the same four.

What was there

Three switches, each a separate boolOrDefault(os.Getenv(...), default) in LoadConfig with the name, the default, and the variable on three lines apiece. Nothing enumerated them, so "what can this deployment turn on" had no answer short of reading LoadConfig.

The four pieces

  • One file. internal/community/featureflags.go holds the table. Each entry binds the environment name, the field it sets, its default, and one sentence on what it turns on, on one line each, so none of the four can drift from the others.
  • One helper. applyFeatureFlags runs the table once, so a switch cannot be read at a call site the reference does not know about.
  • A validator. TestEveryFeatureFlagLivesInTheTable refuses a boolOrDefault anywhere else in the package. Mutation checked: a stray one fails it by file and line.
  • A generated reference. just flags renders agent/rendered/flags.txt, just flags-check and a staleness test fail when it falls behind, and a second test fails when docs/sirens-echo-tuning.md stops linking it.

One deliberate difference from the knobs, now written down

A knob that does not parse keeps its default and is named in the startup log. A switch that does not parse is fatal, because a service running with a surface silently off is worse than one that refuses to start. That is the behaviour it already had; nothing changes except that it is now stated.

Two shape decisions worth reviewing

  • The reference is agent/rendered/flags.txt, not a markdown file under docs/. The issue asks for a markdown doc. docs/ is at 40 files against a 40 cap after the band migration, so a generated page there would break the band the repo just landed on. knobs.txt sits in the same place for the same reason, with the prose page pointing at it.
  • The table takes a *Config rather than package-level vars like the knobs, because these are fields of a per-call Config rather than process globals.

just gate PASS.

closes #854

The numbers have one file, one helper, a validator, and a generated reference. The switches now have the same four. ## What was there Three switches, each a separate `boolOrDefault(os.Getenv(...), default)` in `LoadConfig` with the name, the default, and the variable on three lines apiece. Nothing enumerated them, so "what can this deployment turn on" had no answer short of reading `LoadConfig`. ## The four pieces * **One file.** `internal/community/featureflags.go` holds the table. Each entry binds the environment name, the field it sets, its default, and one sentence on what it turns on, on one line each, so none of the four can drift from the others. * **One helper.** `applyFeatureFlags` runs the table once, so a switch cannot be read at a call site the reference does not know about. * **A validator.** `TestEveryFeatureFlagLivesInTheTable` refuses a `boolOrDefault` anywhere else in the package. Mutation checked: a stray one fails it by file and line. * **A generated reference.** `just flags` renders `agent/rendered/flags.txt`, `just flags-check` and a staleness test fail when it falls behind, and a second test fails when `docs/sirens-echo-tuning.md` stops linking it. ## One deliberate difference from the knobs, now written down A knob that does not parse keeps its default and is named in the startup log. **A switch that does not parse is fatal**, because a service running with a surface silently off is worse than one that refuses to start. That is the behaviour it already had; nothing changes except that it is now stated. ## Two shape decisions worth reviewing * **The reference is `agent/rendered/flags.txt`, not a markdown file under `docs/`.** The issue asks for a markdown doc. `docs/` is at 40 files against a 40 cap after the band migration, so a generated page there would break the band the repo just landed on. `knobs.txt` sits in the same place for the same reason, with the prose page pointing at it. * **The table takes a `*Config`** rather than package-level vars like the knobs, because these are fields of a per-call `Config` rather than process globals. `just gate` PASS. closes #854
feat: give feature switches the config.go treatment
All checks were successful
ci / image-build (pull_request) Successful in 36s
ci / test (pull_request) Successful in 59s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
6efa2d26ad
Every enabled/disabled switch now lives in one table in featureflags.go, binding
the environment name, the field it sets, its default, and what it turns on, on
one line each. They were three separate boolOrDefault calls in LoadConfig with
the name, the default, and the variable spread across three lines apiece, and
nothing could enumerate them.

The validator refuses a switch parsed anywhere outside the table, so the
generated reference cannot silently miss one. Mutation checked: a stray
boolOrDefault elsewhere in the package fails it by file and line.

just flags renders agent/rendered/flags.txt beside knobs.txt, and just
flags-check plus a staleness test fail when it falls behind. A second test
fails when the tuning doc stops linking it, so the prose page and the generated
list cannot drift apart.

One deliberate difference from the knobs, now written down: a knob that does
not parse keeps its default and is named in the startup log, and a switch that
does not parse is fatal, because a service running with a surface silently off
is worse than one that refuses to start. That is unchanged behaviour, now
stated.

The reference is agent/rendered/flags.txt rather than a markdown file under
docs/, matching knobs.txt. docs/ is at 40 files against a 40 cap, so a
generated page there would break the documentation band the repo just migrated
onto.

closes #854

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!925
No description provided.