--- description: Enum-like string values are UPPER_SNAKE everywhere this repo owns the format, JSON included; external standards are converted at the boundary with explicit switch functions --- Decided 2026-10-19 (all repos). An enum-like string value, a member of a string-literal union, is `UPPER_SNAKE` wherever this repo owns the format: TypeScript unions, `profiles.json`, the runtime outcome file, and the `anchors.json` the agent writes per `prompt.md`. explicit-types `upper-snake-string-unions` enforces it and stays on. A value an outside party defines (a CLI flag, an environment variable name, an HTTP API field, a human-readable message) is converted at the boundary by an explicit `switch` function, never by renaming our own values to match it: ```ts export interface ProfileIdentity { type: "CLAUDE" | "CODEX" | "OPENCODE"; model: string; } // The agent name the skills installer knows each profile type by. function skillsAgent(type: RuntimeProfile["type"]): string { switch (type) { case "CLAUDE": return "claude-code"; case "CODEX": return "codex"; case "OPENCODE": return "opencode"; } } ``` In this repo: - Profile `type`: `CLAUDE`, `CODEX`, `OPENCODE` (also in `profiles.json`). `skillsAgent()` (run phase) and `tokenVariable()` (profiles) map them to the installer's agent name and the credential environment variable; `defaultModel()` (credentials) picks a model. - `FailureReason`: `SIGN_IN_FAILED`, `USAGE_EXHAUSTED`, `FAILED`; `toMessage()` gives the words that logs and posted comments show. - Anchor `side`: `NEW` or `OLD` (`prompt.md` tells the agent so); the Gitea review call turns it into `new_position` / `old_position`. A lookup object keyed by enum values would need `UPPER_SNAKE` keys, which explicit-types `no-upper-snake-object-keys` bans, so use a `switch`. TypeScript checks such a switch for exhaustiveness through the declared return type; no `default:` branch ([[lint-config]]).