Configuration management
Skill ats4321/claude-engineering-skills/skills/configuration-management
26 repository-agnostic engineering skills for Claude Code — debugging, design, review, validation, and AI engineering as operational runbooks.
npx -y skills add ats4321/claude-engineering-skills --skill configuration-managementAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Discovering and designing configuration in any repo. Load when asking "where does this value come from?", when adding a new setting, when wiring env vars / .env files / CLI flags / config files, when deciding setting-vs-constant, or when handling secrets (tokens, webhook secrets, API keys). Covers config-source discovery and precedence, immutable/frozen settings objects, startup validation, .env.example documentation, secrets hygiene (never commit .env), and when NOT to add config at all.
SKILL.md
12.6 KB, as published. Nobody here has run it
Configuration Management
Purpose
Find every source of configuration in an unfamiliar repo, understand their precedence, and design new configuration that is discoverable, validated at startup, and immutable at runtime. The doctrine: a value becomes a setting only when someone will actually change it per-deployment — otherwise it is a constant, and constants are simpler.
When to Use / When NOT to Use
Use when:
- You need to know where a runtime value comes from (env var? file? flag? hardcoded default?).
- Adding, renaming, or re-defaulting any setting.
- Touching secrets: tokens, webhook secrets, API keys,
.envfiles. - Behavior differs between environments and you don't know why.
- Designing config for a new service/CLI from scratch.
Do NOT use when:
- The value never changes per-deployment → it's a constant; if you're tempted to make it configurable "for later", load
engineering-minimalisminstead. - The question is about dependency versions → load
dependency-management. - The config controls the build/publish pipeline (dist-tags, package.json scripts) → load
build-and-release. - The setting is a secret whose HANDLING (not location) is in question — e.g. how to validate a webhook secret → load
security-review-playbook. - You're just getting oriented in a new repo → load
codebase-onboardingfirst; come back here for the config layer.
Core Methodology
Step 1 — Inventory every config source
Search the repo for ALL of these, in order (see Discovery Commands):
- Environment variable reads (
os.environ,os.getenv,process.env,env::var). - Dotenv machinery (
.env,.env.example,python-dotenv,dotenvimports). - Config files (
config.py,settings.py,config/,*.toml,*.yaml,*.jsonunder repo root orconfig/). - CLI flags (argparse/typer/click definitions,
--flaghandling, commander/yargs). - Hardcoded defaults and paths (
~/.appname/, literal hosts/ports, magic numbers near I/O).
Write the inventory down. A config source you didn't find is a debugging session you'll have later.
Step 2 — Determine precedence
Establish which source wins when two disagree. Typical (verify, never assume): CLI flag > env var > .env file > config file > hardcoded default. Confirm by reading the load order in the config module — e.g. does dotenv load before or after os.environ is read? Does the code use setdefault (env wins) or assignment (file wins)?
Step 3 — Decide: setting or constant?
Is the value ever different per deployment/user/environment?
├── NO, and no concrete request to change it → CONSTANT.
│ Hardcode it, name it, done. (No config for a value that never changes.)
├── NO, but it tunes behavior you expect to calibrate
│ (batch sizes, chunk limits, timeouts) → SETTING with a default.
│ Document the default in .env.example or --help.
├── YES, and it is NOT secret (hosts, model names, ports)
│ → SETTING via env var with a sane default; document in .env.example.
└── YES, and it IS secret (tokens, webhook secrets, keys)
→ SETTING via env var with NO default, NEVER committed.
├── Add the NAME (not value) to .env.example with a comment.
├── Ensure .env is in .gitignore.
└── Decide startup behavior: fail-fast, or degrade gracefully
with an explicit warning (see Step 5).
Also valid: NO config system at all. A ~700-line local tool with a hardcoded data path and two CLI flags needs zero env plumbing. Minimal config is a feature.
Step 4 — Make settings immutable and centralized
- One module owns config (e.g.
config.py/config.ts). Everything else imports from it — no scatteredos.getenvcalls in business logic. - Load once into a frozen structure: Python
@dataclass(frozen=True)(addslots=Truefor small typed codebases), orObject.freeze/ readonly types in TS. - Expose via a singleton accessor (
get_settings()); construct it at startup, not at import time of every consumer. - Type every field. Parse strings into ints/bools/URLs at load time, not at use time.
Step 5 — Validate at startup
- Required-and-missing → fail fast with a message naming the env var.
- Optional-and-missing → either apply the documented default, or degrade gracefully with an explicit warning (e.g. "GITHUB_TOKEN missing: reviews will be computed but not posted") — never silently.
- Malformed (non-numeric port, bad URL) → fail at startup, not on request N.
Step 6 — Secrets hygiene (non-negotiable)
-
.env(and any file with real secret values) is in.gitignore. Verify withgit check-ignore .env. -
.env.exampleexists, lists every variable NAME with placeholder values and comments (required scope/permissions, defaults). - No secret literal appears in source: grep for
token,secret,key =,Bearerbefore every commit. - Secrets never logged; redact them in error messages and debug output.
- History checked if a secret was EVER committed:
git log -S "<VAR_NAME>" --oneline, thengit show --staton hits — avoidgit log -p, which prints the secret value into your terminal/log again. If leaked, rotate the secret; deleting the file does not un-leak it.
New-setting checklist
- Passed the Step 3 decision tree (state which branch).
- Read in exactly one place (the config module), typed and defaulted there.
- Documented in
.env.example(or--helpfor flags) with its default. - Validated at startup (required → fail fast; optional → default or warn).
- Settings object still frozen; no runtime mutation introduced.
- If secret: no default, gitignored, name-only in
.env.example.
Discovery Commands
# 1. Env var reads (Python / Node / Rust — adjust per repo language)
grep -rn "os.environ\|os.getenv" --include="*.py" .
grep -rn "process.env" --include="*.ts" --include="*.js" . | grep -v node_modules
grep -rn "env::var" --include="*.rs" .
# 2. Dotenv machinery and example files
ls -a .env .env.example .env.local 2>/dev/null
grep -rn "dotenv\|load_dotenv" --include="*.py" --include="*.ts" . | grep -v node_modules
# 3. Config files and modules
find . -maxdepth 2 \( -name "config*" -o -name "settings*" \) -not -path "*/node_modules/*" -not -path "*/.git/*"
ls config/ 2>/dev/null
# 4. CLI flags
grep -rn "add_argument\|typer.Option\|typer.Argument\|click.option" --include="*.py" .
grep -rn '"--' --include="*.js" --include="*.ts" . | grep -v node_modules | head -20
# 5. Hardcoded paths and hosts (defaults hiding as constants)
grep -rn "~/\.\|Path.home()\|os.path.expanduser\|homedir()" --include="*.py" --include="*.ts" --include="*.js" . | grep -v node_modules
grep -rn "localhost:\|127.0.0.1\|0.0.0.0" --include="*.py" --include="*.ts" . | grep -v node_modules
# Secrets hygiene checks
git check-ignore .env && echo "OK: .env ignored" || echo "DANGER: .env not ignored"
git log --all --oneline -- .env # should print NOTHING
grep -rliE "api[_-]?key|secret|token" --include="*.py" --include="*.ts" . | grep -v node_modules # -l: FILENAMES only — never print matched secret values into logs; open flagged files and inspect by eye
Failure Modes & Anti-patterns
| Symptom | Mistake | Correction |
|---|---|---|
| "Works in dev, broken in prod" | Undocumented config source; precedence never established | Run the Step 1 inventory; write down the precedence order; document every var in .env.example. |
| Secret in git history | Committing .env "just once" or a token literal | Gitignore .env BEFORE creating it; if leaked, rotate the secret — deletion is not revocation. |
| Crash on request N, hours after boot | Config parsed lazily at point of use | Validate and type-convert ALL config at startup (Step 5). |
os.getenv scattered across 12 files | No single config owner | Centralize into one frozen settings module; everything imports from it. |
| Setting nobody has ever changed | Config added "for flexibility" | Setting-vs-constant tree (Step 3): no per-deployment variation and no request → constant. Delete the knob. |
| Service silently does nothing when token missing | Missing-secret path unhandled or silent | Fail fast for required secrets; for optional ones, warn explicitly and degrade visibly. |
| Test mutates settings, later tests flake | Mutable global settings object | Frozen dataclass / Object.freeze; tests construct their own instance. |
| New contributor can't boot the app | .env.example missing or stale | .env.example is the setup contract: every var name, placeholder, comment, default. |
Repository Examples
prism — the full config pattern (as of 2026-07-04)
~/prism: Python 3.10+ FastAPI self-hosted AI PR reviewer on local Ollama. Config is centralized in prism/config.py: python-dotenv loads .env, values land in a frozen dataclass Settings exposed through a get_settings() singleton — Steps 4–5 exactly. .env.example documents every variable:
GITHUB_TOKEN— PAT withpull_requests:write(secret, no default; when missing the app warns and skips the API call — graceful degradation, not silence)GITHUB_WEBHOOK_SECRET— secret, used for HMAC validationOLLAMA_HOST— defaulthttp://localhost:11434OLLAMA_MODEL— defaultllama3.2MAX_FILES_PER_PR— default 10;MAX_LINES_PER_CHUNK— default 120: deliberately settings, not constants, because they tune review behavior per deployment (Step 3, "calibration" branch)
Contrast within the same repo: the GitHub API version is pinned as a hardcoded header value "2022-11-28" — a constant, because it changes with code (API compatibility), not with deployment. Entry point prism = "prism.server:run" boots uvicorn on 0.0.0.0:8000.
agentix — zero-config as a valid design (as of 2026-07-04)
~/agentix: ~700-line local ReAct agent framework. NO env vars, NO config files. Storage path is hardcoded (~/.agentix/memory.db); the only knobs are CLI flags (--context-window, --model). For a single-user local tool this is the correct amount of configuration: the Step 3 tree resolves almost everything to "constant" or "CLI flag". Minimalism doctrine applied to config itself.
ragit — derived path convention (as of 2026-07-04)
~/ragit stores its index at ~/.ragit/<sha256-prefix>/ — a hardcoded convention (constant), not a setting. No one asked to relocate it, so no knob exists.
Validation Criteria
You applied this skill correctly if:
- You can list every config source in the repo and state their precedence, backed by the load-order code, not guesses.
- Every setting you added appears in
.env.example(or--help) with its default, and is read in exactly one module. - The app fails at startup — with a variable-named message — when a required setting is missing or malformed.
git check-ignore .envpasses andgit log --all -- .envis empty.- For each new knob, you can name the Step 3 branch that justified it; anything on the "constant" branch has no knob.
Provenance & Maintenance
- Sources: ~/prism, ~/agentix, ~/ragit — investigated 2026-07-04. prism and agentix directories were read-restricted during authoring; their facts come from the 2026-07-04 verified fact pack ("verified 2026-07-04, not re-read"). ragit was partially re-read this session.
- Assumptions: prism's precedence (dotenv → frozen Settings) inferred from the verified description of
prism/config.py; exact load-order nuances (e.g. whether real env vars override.env) were not re-read — "(hypothesis — requires verification)". - Re-verification commands:
cat ~/prism/.env.example grep -n "frozen\|load_dotenv\|get_settings" ~/prism/prism/config.py grep -rn "getenv\|environ" ~/agentix --include="*.py" # expect ~zero hits grep -rn "\.ragit" ~/ragit/ragit --include="*.py" - Likely to drift: prism's default values (MAX_FILES_PER_PR, OLLAMA_MODEL); agentix may grow env-var config if it ever needs per-deployment variation; the pinned GitHub API version header.
- Maintenance checklist: re-run re-verification; confirm
.env.examplein prism still matchesSettingsfields one-to-one; confirm agentix is still zero-config before citing it; update defaults quoted here if they change.