Skip to content

v1.16.0.0 feat: /env-check — pre-ship env var sanity check (#1106)#1228

Open
gregario wants to merge 3 commits intogarrytan:mainfrom
gregario:feat/1106-env-check-skill
Open

v1.16.0.0 feat: /env-check — pre-ship env var sanity check (#1106)#1228
gregario wants to merge 3 commits intogarrytan:mainfrom
gregario:feat/1106-env-check-skill

Conversation

@gregario
Copy link
Copy Markdown
Contributor

Summary

Closes #1106. Reported by @tashonna-labs.

Env var drift is the quiet bug that breaks deploys: a new process.env.X nobody added to .env.example, a stale .env.example your teammate clones tomorrow, an API key that drifted into a tracked file. None of it shows up in tests. /cso catches it eventually but /cso is the deep audit, not the pre-flight. New /env-check is the 5-second pre-flight before /ship.

Persona derivation

Tashonna's framing in the issue:

  • "Env var drift is one of those quiet problems that bites you at deployment time."
  • "Run before /ship to catch accidental secret commits or undocumented env dependencies before they reach CI."
  • "Probably a 100-line Python/Node script that Claude can run directly."

Not a security audit. A pre-ship sanity check. The persona derived from those three signals:

"You are the teammate who reads a PR and asks: did you add this to .env.example?"

Calm, matter-of-fact, informational. CRITICAL is reserved for the actual deploy-breaking case (hardcoded secret in tracked source). Otherwise the tone is "did you mean to commit this?" not "SECURITY VIOLATION DETECTED."

What it does

Four steps, three findings sections, one verdict.

Step What
1 Detect env files (.env.example / .sample / .template) and stack patterns (Node/TS, Deno, Python, Ruby, Go, Rust, .NET, JVM, shell)
2 Drift — keys in .env.example vs .env. MEDIUM if missing from .env, LOW if undocumented in .env.example.
3 Code referencesprocess.env.X, os.getenv, Deno.env.get, ENV[], os.Getenv, std::env::var, Environment.GetEnvironmentVariable, System.getenv. Cross-reference against .env.example. HIGH for orphaned references (the actual deploy-breaking pattern).
4 Hardcoded secrets in tracked source — sk-, sk-ant-, ghp_, AKIA, xoxb-, sk_live_, AIza, JWTs, postgres/mongo/mysql/redis URLs with creds, RSA/EC/DSA/OpenSSH private key headers. CRITICAL when found.

Then a Pre-Ship Env Check report with three sections, severity counts, and one verdict: SHIP-READY, NEEDS REVIEW, or DO NOT SHIP. Soft verdict — informational, never blocks.

Modes

  • /env-check — full check
  • /env-check --drift — only .env.example.env comparison
  • /env-check --refs — only orphaned code references
  • /env-check --secrets — only hardcoded-secret scan
  • /env-check --diff — only files changed on this branch (combinable)

FP rules built in

  • Runtime-provided keysNODE_ENV, RAILS_ENV, CI, GITHUB_*, GITLAB_*, VERCEL_*, NETLIFY_*, RENDER_*, FLY_APP_NAME, etc. — never flagged as HIGH (auto-injected, not project-secret)
  • Public-by-design prefixesNEXT_PUBLIC_*, VITE_*, REACT_APP_*, PUBLIC_*, EXPO_PUBLIC_* — LOW max
  • Placeholder values — "your_", "changeme", "TODO", "xxx", "<your-key-here>" — excluded from secret scan
  • Test fixtures*test*, __tests__/*, spec/*, fixtures/* — MEDIUM max unless same fingerprint in non-test code
  • Local env files.env.local, .env.*.local, .env.test, .env.development — LOW max regardless

What's NOT in this PR (deferred)

  • Auto-fix mode (adding missing keys to .env.example) — issue mentions but kept narrow for first PR
  • Trend tracking via JSONL — that's audit-skill machinery; this skill runs every PR, doesn't need a long history
  • Git-history archaeology/cso's job
  • Integration as /cso --env subphase — let it stand alone; mention the relationship in skill body

Style

Follows /cso and /health conventions:

  • Persona statement → hard gate → ## User-invocable## Arguments → numbered Steps → ## Important Rules
  • ASCII-art findings table with ═══ borders
  • Severity ladder with explicit FP rules
  • {{PREAMBLE}}, {{LEARNINGS_SEARCH}}, {{LEARNINGS_LOG}} resolver tags

Calibrated to the issue author's "lightweight" framing — 255 lines for the template, not the 600+ that /cso packs. Different jobs.

Test plan

  • bun run gen:skill-docs --host all — regenerates cleanly for claude, codex, factory, kiro, opencode, slate, cursor, openclaw, hermes, gbrain
  • bun test test/skill-validation.test.ts test/gen-skill-docs.test.ts — 689 pass, 0 fail
    • skill-validation: frontmatter, allowed-tools, triggers, name uniqueness
    • gen-skill-docs: zsh-glob safety, all-host dry-run freshness, token ceiling
  • Auto-discovered by discoverTemplates in scripts/discover-skills.ts — no setup script wiring needed beyond dropping env-check/SKILL.md.tmpl

Bisected commits

  1. feat: /env-check — pre-ship env var sanity checkenv-check/SKILL.md.tmpl only
  2. chore(env-check): regenerate SKILL.md from template — generated file
  3. chore: release v1.16.0.0 (/env-check skill) — VERSION + CHANGELOG

Files

  • env-check/SKILL.md.tmpl (new, 255 lines)
  • env-check/SKILL.md (regenerated for claude host, ~1300 lines after preamble expansion)
  • VERSION, CHANGELOG.md — release v1.16.0.0

🤖 Generated with Claude Code

gregario and others added 3 commits April 26, 2026 21:11
Closes garrytan#1106. Reported by @tashonna-labs.

Env var drift is the quiet bug that breaks deploys: a new `process.env.X`
that nobody added to `.env.example`, a stale `.env.example` your teammate
clones tomorrow, an API key that drifted into a tracked file. None of it
shows up in tests. /cso would catch it eventually but /cso is the deep
audit, not the pre-flight.

This is the pre-flight. Four steps, three findings sections, one verdict.
Run before /ship. Read-only.

Persona is "the teammate who reads a PR and asks: did you add this to
.env.example?" — derived from the issue's framing ("quiet problems that
bites you at deployment time", "Run before /ship to catch ... before they
reach CI"). The voice is matter-of-fact, not alarmist; the report is
informational, not gating; CRITICAL is reserved for the actual deploy-
breaking case (hardcoded secret in tracked source).

What it checks:

  1. **Drift** — keys in `.env.example` vs `.env` (MEDIUM if missing,
     LOW if undocumented).
  2. **Code references** — `process.env.X`, `os.getenv`, `Deno.env.get`,
     `ENV[]`, `os.Getenv`, `std::env::var`, `Environment.GetEnvironmentVariable`,
     `System.getenv`. Cross-referenced against `.env.example`. HIGH for
     orphaned references (the actual deploy-breaking pattern).
  3. **Hardcoded secrets** — `sk-`, `sk-ant-`, `ghp_`, `AKIA`, `xoxb-`,
     `sk_live_`, `AIza`, JWTs, postgres/mongo/mysql/redis URLs with creds,
     private key headers. CRITICAL when found in tracked source.

What it doesn't do (deferred):

- Auto-fix (adding missing keys to .env.example) — issue mentions but
  scope-cut to keep first PR narrow
- Trend tracking via JSONL — audit-skill machinery; this skill is the
  pre-flight, runs every PR, doesn't need a long history
- Git-history archaeology — that's /cso's job

Modes:

- `/env-check` — full check
- `/env-check --drift` — only .env.example ↔ .env comparison
- `/env-check --refs` — only orphaned code references
- `/env-check --secrets` — only hardcoded-secret scan
- `/env-check --diff` — only files changed on this branch (combinable)

FP rules cover runtime-provided keys (NODE_ENV, CI/*, cloud-provider
auto-injected vars), public-by-design prefixes (NEXT_PUBLIC_*, VITE_*,
PUBLIC_*), placeholder values in example files, and test fixtures.

Style follows /cso and /health: persona statement, hard gate, scope
flags, severity ladder with FP rules, ASCII-art findings table. ~250
lines for the template — calibrated to the issue author's "lightweight
script" framing, not a /cso-class deep dive.

Auto-discovered by `discoverTemplates` in `scripts/discover-skills.ts` —
no setup wiring needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
bun run gen:skill-docs --host all

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add /env-check — validate .env.example completeness and detect hardcoded secrets

1 participant