The drift guard added in #583 (tests/unit/env-documentation.test.ts) extracts environment variables by matching literal process.env.NAME. That is the minority pattern in this repository, so the guard covers less than its name suggests, and its header comment describes the gap as an edge case when it is most of the gap.
Measured on main at 75e08e0. .env.example documents 56 variables. The extractor sees 40. Of the 22 it misses, 16 are statically resolvable and only 6 are genuinely out of reach:
| shape |
count |
example |
same-file const X = "NAME", then process.env[X] |
6 |
AGENT_ENABLED_ENV in src/lib/agent/config.ts |
table field, then process.env[spec.field] |
10 |
maxVar: "RATE_LIMIT_QUERY_MAX" in src/lib/api/rate-limit.ts |
| reached through a helper taking the name as an argument |
6 |
getEnvVar("LLM_PROVIDER") in src/lib/llm/utils/config.ts |
The six recoverable by constant are LIBREDB_AGENT_ENABLED, LIBREDB_AGENT_THREAD_CONTEXT, AGENT_MODEL_TUNING_PATH, AGENT_MODEL_TURN_TIMEOUT_MS, WORKFLOW_LOCAL_DATA_DIR and WORKFLOW_TARGET_WORLD. The ten by table field are the RATE_LIMIT_* pairs. The remaining six are HOSTNAME, MY_DB_PASSWORD and the four LLM_* names.
Why this matters more than the numbers suggest. The guard exists because LOG_LEVEL drifted. Had src/lib/logger.ts read it through a named constant instead of process.env.LOG_LEVEL, the guard would not have caught the very drift it was written for. A guard whose blind spot covers a third of the surface reports green for a reason the reader cannot see.
Fix. Extend the extractor to two more shapes, both of which are string literals in the same file as the read:
- collect
const X = "NAME" (exported or not), then resolve process.env[X];
- collect object fields whose value is a bare uppercase string literal, then resolve
process.env[<expr>.field].
Then rewrite the header comment to state what remains uncovered and roughly how much, rather than naming dynamic reads as an aside. Helper-argument reads like getEnvVar("LLM_PROVIDER") stay out of scope; say so and say why.
Done when the extractor finds the sixteen names listed above in addition to what it finds today, each shape is pinned by naming a specific variable rather than by asserting a count, the header comment describes the real boundary, and bun run test is green. One test file, no product code.
Curated for Hacktoberfest 2026. @dchaudhari7177 wrote the guard in #583 and gets first refusal; comment to claim it if you want it, otherwise it is open to anyone after a few days. A PR must reference this issue and land with its tests in the same change; see CONTRIBUTING.md. Repo rules that apply: run bun run test (never bare bun test), and the 100% line-coverage gate must stay green. Local bun run test also needs the helm binary on PATH for unrelated chart tests - see #570 if it bites.
The drift guard added in #583 (
tests/unit/env-documentation.test.ts) extracts environment variables by matching literalprocess.env.NAME. That is the minority pattern in this repository, so the guard covers less than its name suggests, and its header comment describes the gap as an edge case when it is most of the gap.Measured on
mainat 75e08e0..env.exampledocuments 56 variables. The extractor sees 40. Of the 22 it misses, 16 are statically resolvable and only 6 are genuinely out of reach:const X = "NAME", thenprocess.env[X]AGENT_ENABLED_ENVinsrc/lib/agent/config.tsprocess.env[spec.field]maxVar: "RATE_LIMIT_QUERY_MAX"insrc/lib/api/rate-limit.tsgetEnvVar("LLM_PROVIDER")insrc/lib/llm/utils/config.tsThe six recoverable by constant are
LIBREDB_AGENT_ENABLED,LIBREDB_AGENT_THREAD_CONTEXT,AGENT_MODEL_TUNING_PATH,AGENT_MODEL_TURN_TIMEOUT_MS,WORKFLOW_LOCAL_DATA_DIRandWORKFLOW_TARGET_WORLD. The ten by table field are theRATE_LIMIT_*pairs. The remaining six areHOSTNAME,MY_DB_PASSWORDand the fourLLM_*names.Why this matters more than the numbers suggest. The guard exists because
LOG_LEVELdrifted. Hadsrc/lib/logger.tsread it through a named constant instead ofprocess.env.LOG_LEVEL, the guard would not have caught the very drift it was written for. A guard whose blind spot covers a third of the surface reports green for a reason the reader cannot see.Fix. Extend the extractor to two more shapes, both of which are string literals in the same file as the read:
const X = "NAME"(exported or not), then resolveprocess.env[X];process.env[<expr>.field].Then rewrite the header comment to state what remains uncovered and roughly how much, rather than naming dynamic reads as an aside. Helper-argument reads like
getEnvVar("LLM_PROVIDER")stay out of scope; say so and say why.Done when the extractor finds the sixteen names listed above in addition to what it finds today, each shape is pinned by naming a specific variable rather than by asserting a count, the header comment describes the real boundary, and
bun run testis green. One test file, no product code.Curated for Hacktoberfest 2026. @dchaudhari7177 wrote the guard in #583 and gets first refusal; comment to claim it if you want it, otherwise it is open to anyone after a few days. A PR must reference this issue and land with its tests in the same change; see CONTRIBUTING.md. Repo rules that apply: run
bun run test(never barebun test), and the 100% line-coverage gate must stay green. Localbun run testalso needs thehelmbinary on PATH for unrelated chart tests - see #570 if it bites.