Repository navigation
Every environment variable carries its program name twice: WRIT_WRIT_VERBOSE works, WRIT_VERBOSE does not #763
Description
Feature: #456 — Configuration surfaces in the CLIs
Every environment variable in the suite carries its program's name twice. WRIT_WRIT_VERBOSE works;
WRIT_VERBOSE does not.
Measured
$ WRIT_VERBOSE=true writ status -o json # no effect
$ WRIT_WRIT_VERBOSE=true writ status -o json # "Using config: ..." — it took
Cause
Two mechanisms each add the program name, and neither knows about the other.
--verbose the flag
↓ BindFlags: key = toolName + "." + f.Name cmd/internal/cli/viper.go:113
writ.verbose the viper key
↓ SetEnvKeyReplacer(".", "_") then upper-cased viper.go:81
WRIT_VERBOSE
↓ SetEnvPrefix("WRIT") prepends viper.go:79
WRIT_WRIT_VERBOSE the variable a user must set
BindFlags namespaces the key because UseSharedConfig puts all four programs in one config file, where
writ.verbose and lore.verbose must not collide. InitViper then sets the env prefix to the same program
name. Each is right on its own.
The documented contract says otherwise, in two places
cmd/internal/cli/viper.go:80:
Replace dots with underscores for nested keys:
writ.vars.user_name→WRIT_VARS_USER_NAME
schema/devlore-config.json:122:
Example:
model.provider→DEVLORE_MODEL_PROVIDER
Both describe single prefixing. Neither matches the binary. RULED 2026-08-31: the binary is wrong.
Scope: all four programs, every flag
Any persistent flag bound through BindFlags is affected — --config, --verbose, --output, --store,
--filter, --jq, and the model flags, on devlore-test, lore, star, and writ.
The one env path that behaves as documented is WRIT_SEGMENT_<NAME>, and only because
segment.LoadFromEnv reads os.Getenv directly (detect.go:39) rather than going through viper.
Fix
Strip the program name from one of the two. The key must stay namespaced — the shared config file requires
it — so the env prefix is what goes: SetEnvPrefix becomes DEVLORE, or is dropped in favour of a key
replacer that maps writ.verbose to WRIT_VERBOSE directly.
DEVLORE as the prefix has a second argument for it: schema/devlore-config.json:122 already documents
DEVLORE_MODEL_PROVIDER, so shared keys read correctly, and a program-scoped key becomes
DEVLORE_WRIT_VERBOSE — which says what it is.
This is breaking
Every environment variable in use today stops working. Under the governing principle a variable retires by
deletion rather than by accepting both spellings — an alias here would mean carrying the doubled form
forever.
Worth knowing what is actually at risk: WRIT_WRIT_* is undocumented and contradicts both doc comments, so
anything relying on it was written by reading the source or by trial and error.
Verification
WRIT_VERBOSE=true writ statusreports the config file in use — orDEVLORE_WRIT_VERBOSE, per the shape
chosen.- The same flag on
lore,star, anddevlore-testresolves under its own program's name with no doubling. - A shared key resolves once, not once per program.
WRIT_SEGMENT_<NAME>still works — it does not route through viper.- A test asserts the mapping, so the contract stops being maintained by reading two comments that disagree
with the binary.
Related
Found while designing --scope for #762, which needs a working environment path. Same class as #746, #753,
and #754: a declared surface the code does not honour.
Activity