- **Constant unit suffixes**: Time/size constants MUST include unit in name: `_MS`, `_SECONDS`, `_BYTES`. Counts/ratios/strings: no suffix. No inline `// Ms` comments — the name IS the documentation
- **Async**: Use async/await only for genuinely asynchronous operations. Do not wrap synchronous code in Promises or mark methods `async` without `await`. Fire-and-forget wrapped in `void`; handle rejections with try/catch. Interface methods return `Promise` only when at least one implementation is genuinely async (e.g., `AuthStrategy.authenticate()` — remote strategy does network I/O)
- **Error handling**: Typed errors (`BaseError`, `OCPPError`) with structured properties; avoid generic `Error`; use `instanceof` guards (not `as` casts)
-- **`new Error()` in utils/, worker/, and auth/cache/**: Acceptable — `BaseError` lives in `exception/` which cannot be imported from these modules without circular dependencies (see Common Pitfalls)
+- **`new Error()` in utils/, worker/, and auth/cache/**: Acceptable (circular dependency constraint)
- **Null safety**: Avoid non-null assertions (`!`); use optional chaining (`?.`) and nullish coalescing (`??`)
- **Type safety**: Prefer explicit types over `any`; use type guards and discriminated unions; no `as any`, `@ts-ignore`, `@ts-expect-error`
## Utility Usage Rules
- **Emptiness checks**: Use `isEmpty()` / `isNotEmptyArray()` instead of `.length === 0` / `.size > 0` (except in worker/)
-- **Number parsing**: Use `convertToInt()` / `convertToFloat()` instead of `Number.parseInt()` / `Number.parseFloat()`. Exception: when NaN fallback is needed (e.g., `getLimitFromSampledValueTemplateCustomValue` — keep `Number.parseFloat`)
+- **Number parsing**: Use `convertToInt()` / `convertToFloat()` instead of `Number.parseInt()` / `Number.parseFloat()`. Exception: when NaN fallback is intentional
- **Cloning**: Use `clone()` — never `JSON.parse(JSON.stringify())`
- **Random**: Use `secureRandom()` / `generateUUID()` — not direct `randomBytes()` / `randomUUID()` (except in worker/ which has its own copies)
- **Format**: `${chargingStation.logPrefix()} ${moduleName}.methodName: Message`
- **Daily rotation** enabled by default; configurable max files/size
- **Log config**: `src/utils/Configuration.ts` canonical defaults
-- **`console.*` is acceptable ONLY** in: `start.ts` (entry point), `Configuration.ts`/`ConfigurationMigration.ts` (static init, logger not ready), `ErrorUtils.ts` (uncaught handlers, logger may be null), `WorkerUtils.ts` (standalone, no logger access). Everywhere else: use `logger`
+- **`console.*` is acceptable ONLY** in: `start.ts`, `Configuration.ts`, `ConfigurationMigration.ts`, `ErrorUtils.ts`, `WorkerUtils.ts`. Everywhere else: use `logger`
## Configuration
- **ESLint cache**: Clear `.eslintcache` if lint results seem stale after config changes
- **Web UI is independent**: Always run its quality gates separately from `ui/web/` directory
- **OCPP server is Python**: Uses Poetry, not pnpm. Linter is ruff (not pylint/flake8). Type checker is mypy
-- **Barrel circular deps**: `src/utils/` must NOT import from `src/exception/index.js` — causes circular via `OCPPError → OCPPConstants → utils/Constants`
+- **Barrel circular deps**: `src/utils/` must NOT import from `src/exception/index.js`
## OCPP Spec Search (QMD)
-`docs/` is indexed as QMD collection `ocpp-specs` (24 markdown files, 3213 chunks).
+Collection `ocpp-specs` — OCPP 1.6, 2.0.1, 2.1 specs from `docs/` (24 files, 3213 chunks).
-```bash
-# BM25 keyword search (always works)
-qmd search "D01.FR SendLocalList requirements" --collections ocpp-specs
-
-# Hybrid search with LLM expand + rerank (needs env -u CI in agent context)
-env -u CI qmd query "what happens when LocalAuthListEnabled is disabled" --collections ocpp-specs
+**Project-specific gotchas**:
-# Vector similarity only
-qmd vsearch "functional requirements for local authorization list" --collections ocpp-specs
-```
+- `CI=true` disables LLM ops → prefix `env -u CI` on all `qmd query` / `qmd vsearch` calls. `qmd search` (BM25 only) works without it.
+- Collection flag: `-c` or `--collection` (singular).
+- Result limit flag: `-n`.
Re-index after spec updates: `qmd update --pull && qmd embed`
-**Setup**: QMD stores absolute paths. Use a stable path:
-
-```bash
-qmd collection add /Users/I339261/SAPDevelop/e-mobility-charging-stations-simulator-git/docs --name ocpp-specs
-qmd embed
-```
-
---
## Docker