CtrlK
BlogDocsLog inGet started
Tessl Logo

config-consistency-checker

Detects inconsistencies across configuration files, environments, and deployment manifests — missing keys, drifted values, type mismatches. Use when debugging why staging behaves differently from production, before a deploy to catch config drift, or when auditing multi-environment configs.

Install with Tessl CLI

npx tessl i github:santosomar/general-secure-coding-agent-skills --skill config-consistency-checker
What are skills?

100

Quality

100%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SKILL.md
Review
Evals

Config Consistency Checker

"Works in staging, broken in prod" is almost always a config diff. Find it before deploy, not after.

What to compare

AxisCompareLooking for
Across environmentsstaging.yamlproduction.yamlKeys present in one, missing in other
Config file ↔ codeConfig keys ↔ config.get(...) / os.environ[...] callsDead keys (in config, never read) and missing keys (read, never defined)
Config file ↔ deploymentApp config ↔ K8s ConfigMap/Deployment envConfig expects FOO; deployment sets FOO_URL
Config file ↔ schema/defaultsLoaded values ↔ type annotations / validators"30" where the code expects 30
Across replicas (runtime)Live config on pod A ↔ pod BPartial rollout, stale ConfigMap mount

The key-diff algorithm

  1. Flatten each config to dotted.key.path → value pairs.
  2. Set-diff the key sets.
  3. For keys in both, value-diff.
  4. Classify each difference.
staging_keys − production_keys   → "missing in prod" (usually a bug)
production_keys − staging_keys   → "missing in staging" (usually a bug, unless it's a prod-only integration)
staging[k] ≠ production[k]       → "differs" — intentional or drift?

Classifying differences: intentional vs. drift

Not every difference is a bug. The signal is whether the difference is explicable:

Difference classIntentional?
Database host differsIntentional — they're different databases
Pool size differsMaybe — prod should be ≥ staging; if prod is smaller, bug
Log level differsIntentional (prod=INFO, dev=DEBUG). If prod=DEBUG, bug
Feature flag differsDepends entirely on the flag — ask
Key exists in one, not the otherAlmost always drift — someone forgot to add it to both
Typo'd key (close string distance)Always driftdatabse_host is reading the default

Worked example

Files: config/staging.yaml, config/production.yaml

Flattened:

staging:
  db.host                = staging-db.internal
  db.pool_size           = 20
  cache.ttl_seconds      = 300
  feature.new_checkout   = true
  log.level              = debug

production:
  db.host                = prod-db.internal
  db.pool_size           = 10
  cache.ttl_seconds      = 300
  feature.new_checkout   = true
  log.level              = info
  sentry.dsn             = https://...

Diff:

KeyStagingProdVerdict
db.hostdiffersdiffers✅ Intentional — different databases
db.pool_size2010⚠️ Prod pool smaller than staging — probably wrong
cache.ttl_seconds300300✅ Match
feature.new_checkouttruetrue✅ Match
log.leveldebuginfo✅ Intentional — expected split
sentry.dsnset⚠️ Missing in staging — errors from staging go nowhere

Two flags, both worth raising.

Code ↔ config cross-check

Grep the codebase for config reads:

rg 'config\[|config\.get|os\.environ\[|getenv\(' --type py

Extract the key names. Compare against what's actually in the config files:

  • In code, not in config: The app will hit a default or crash. Which one? Read the code.
  • In config, not in code: Dead config. Delete it, or it's a typo for something the code does read.

Edge cases

  • Secrets show as "differs" because they're redacted: Compare key presence, not value, for anything marked secret. password: ***password: *** by string comparison but they're both set — that's what matters.
  • Array ordering differs: [a, b] vs [b, a] — depends on semantics. A list of allowed origins is a set (order irrelevant); a list of middleware is a sequence (order critical). You have to know which.
  • One config is generated (Helm/Kustomize), the other is hand-written: Compare the rendered output, not the template. helm template / kustomize build first.
  • The app has fallback defaults in code: A missing key isn't an error if the code has config.get("foo", sensible_default). But flag it — implicit defaults drift silently.

Do not

  • Do not report "47 keys differ" without classifying. The user wants the 2 that are bugs, not the 45 that are intentional environment splits.
  • Do not compare secret values. Compare presence. Never echo a secret value in the diff output.
  • Do not trust the config file as source of truth. The code's reads are the truth; the config is what's supposed to satisfy them.
  • Do not ignore near-miss key names. database_url in config and DATABASE_URI in code — Levenshtein distance 2, probably the bug.

Output format

## Missing keys
  <key>  — in <envA>, missing from <envB>  [<likely intentional | likely drift>]

## Suspicious value diffs
  <key>  <envA>=<val>  <envB>=<val>  — <why suspicious>

## Dead config (in file, never read by code)
  <key>

## Unbacked reads (in code, never in config — will default or crash)
  <file>:<line>  reads <key>  — default: <value | NONE — will crash>

## Clean
  <N> keys match across all environments
Repository
santosomar/general-secure-coding-agent-skills
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.