# Implementer — F-004 Typed config and feature flags done -> work/artifacts/F-004/implementer.md ## Files created - src/infrastructure/config/config.ts (loadConfig pure over env object; ConfigError accumulates all problems; names var names only) - src/infrastructure/config/tests/config.test.ts (8 tests) - src/modules/flags/domain/feature-flag-store.ts (FeatureFlagProvider + InMemoryFeatureFlagStore + createFlagStore) - src/modules/flags/index.ts (module public API) - src/modules/flags/tests/feature-flag-store.test.ts (5 tests) - src/app/tests/config-flags.test.ts (2 composition tests: default OFF, runtime flip without redeploy) ## Files modified - src/app/build-app.ts: optional flags dep; `app.flags` decorated on the instance (typed via module augmentation) - src/infrastructure/http/server.ts: loadConfig fail-fast before anything starts; typed port/host/logLevel; flags seeded from FLAG_* env - .env.example: REDIS_URL + FLAG_EXAMPLE_FEATURE example - README.md: Configuration section ## API changes - Server now REQUIRES DATABASE_URL at startup (fail-fast; intentional per spec). ## Bug found by tests during build (fixed before gates) - Flag store constructor did not lowercase keys while isEnabled lowercased lookups -> case-insensitive seed test failed. Fixed by normalizing keys in the constructor. Regression test retained. ## Tests passed (evidence) ``` npm run lint -> OK npm run lint:boundaries -> Boundary check OK: 22 file(s) checked (flags module imports nothing outside its subtree) npm run typecheck -> exit 0 npm run build -> exit 0 npm test -> 9 files passed, 2 skipped (integration without DB); 38 passed | 6 skipped npm run test:integration-> 2 files, 6 passed (no regression) live smoke: env -u DATABASE_URL node dist/.../server.js -> "Invalid configuration:\n- DATABASE_URL is required", exit 1 (AC1) with DATABASE_URL + FLAG_EXAMPLE_FEATURE=true -> health=200, "HTTP server listening" ./scripts/verify.sh -> exit 0 ``` ## Known limitations - No admin endpoint to flip flags yet (scope out); store API is runtime-mutable so the first consumer only adds transport. - Flags live in memory: restart reseeds from env (documented as expected; persistence is a later ticket if needed). ## Follow-up work - F-005 identity consumes config.databaseUrl through the pool and can guard risky paths via app.flags.