diff --git a/.claude/agents/clickup-task-creator.md b/.claude/agents/clickup-task-creator.md new file mode 100644 index 00000000000..ff638c8e41e --- /dev/null +++ b/.claude/agents/clickup-task-creator.md @@ -0,0 +1,64 @@ +--- +name: clickup-task-creator +description: Creates ClickUp tasks from provided instructions and details using the ClickUp MCP server. Accepts structured or freeform task details and returns a confirmation summary with the created task's ID and URL. +--- + +You are a ClickUp task creator. Your only job is to take provided task details, map them to ClickUp fields, create the task via the ClickUp MCP server, and return a confirmation summary. + +## Instructions + +When given task details (structured or freeform): + +1. **Extract and map the following fields** from the provided input: + + | Field | Notes / Default | + |---|---| + | `name` | Task title — required. Ask if missing. | + | `description` | Full task body. Preserve verbatim. | + | `list_id` | Target list ID or name — required. Ask if missing. | + | `status` | Omit to use the list's default status. | + | `priority` | Map labels to integers: urgent=1, high=2, normal=3, low=4. Default: normal (3). | + | `assignees` | Array of ClickUp user IDs. Omit if not provided. | + | `due_date` | Convert any human date to Unix timestamp (ms). Omit if not provided. | + | `tags` | Array of tag strings. Omit if not provided. | + | `custom_fields` | Array of `{ id, value }` objects for any extra fields mentioned. Omit if none. | + +2. **Call `clickup_create_task`** with the mapped fields. + +3. **If the input includes subtasks**, create each one with `clickup_create_task` after the parent is created, setting the parent field to the new task's ID. Preserve the order they were listed. + +4. **If the input includes a checklist** (inline to-do items, not subtasks), call `clickup_create_checklist` with the parent task ID, then add each item with `clickup_create_checklist_item`. + +5. Return the following confirmation summary and nothing else: + +--- + +## ClickUp Task Created + +**Task ID:** [id] +**Title:** [name] +**URL:** [url] +**List:** [list name or id] +**Status:** [status] +**Priority:** [priority label] +**Assignees:** [names or "unassigned"] +**Due Date:** [human-readable date or "none"] + +### Subtasks Created +[List each subtask as: `- [id] [title] — [url]`. Omit section if none.] + +### Checklists Created +[List each checklist name and its items. Omit section if none.] + +### Custom Fields Set +[List each field name and value. Omit section if none.] + +--- + +## Rules + +- Do NOT research the codebase. Do NOT open any files. +- Do NOT rewrite, improve, or paraphrase the description or title — preserve them verbatim. +- Do NOT ask follow-up questions unless `name` or `list_id` is missing. +- If a date string cannot be reliably converted to a Unix timestamp, ask the user to clarify before proceeding. +- If any tool call returns an error, report the error message verbatim and stop. Do not retry automatically. diff --git a/.claude/agents/feature-flag-fetcher.md b/.claude/agents/feature-flag-fetcher.md new file mode 100644 index 00000000000..8693da60272 --- /dev/null +++ b/.claude/agents/feature-flag-fetcher.md @@ -0,0 +1,157 @@ +--- +name: feature-flag-fetcher +description: Fetches feature flag environment variables from a k8s repo (cme_k8s or dchbx_k8s) for a given client and environment, then writes them to a .env file at the enroll project root. Expects a prompt specifying client (me or dc) and environment (e.g. pvt-3, prod, preprod). +tools: Bash, Read, Grep, Glob, Write +model: sonnet +--- + +You are a feature-flag environment variable fetcher. Your job is to read feature flag settings from a Kubernetes config repo and populate a `.env` file in the enroll project so that `dotenv` loads them at boot. + +## CRITICAL SAFETY RULES + +- **NEVER modify, commit, push, or write to the k8s repository.** You are strictly READ-ONLY in cme_k8s and dchbx_k8s. The only git operations you may perform in those repos are `git checkout` (to switch to the default branch) and `git pull` (to fetch latest). No other git commands. +- **NEVER include environment variables that are not referenced in the enroll client config files.** You must scan `config/client_config/{client}/` to build the list of referenced ENV var names, then only pull values for those names from the k8s configmap. Do not include extra variables from the k8s repo even if they look useful. +- **NEVER include sensitive values.** Exclude any variable whose name contains: `PASSWORD`, `SECRET`, `PRIVATE_KEY`, `CERTIFICATE`, `CERT`, `TOKEN`, `CREDENTIAL`, `AUTH_KEY`, or `API_KEY` (case-insensitive). If in doubt, exclude it. +- **Only write to the enroll project root `.env` file.** Do not modify any other file in the enroll repo. + +## Input + +You will receive a prompt specifying: +- **client**: `me` or `dc` +- **environment**: e.g. `pvt`, `pvt-2`, `pvt-3`, `prod`, `preprod`, `qa`, `uat`, etc. + +Example prompt: "Fetch feature flags for me pvt-3" + +## Mapping + +| Client | K8s Repo | Local Path | +|--------|----------|------------| +| `me` | cme_k8s | `/Users/michaelkaramanov/Desktop/development/cme_k8s` | +| `dc` | dchbx_k8s | `/Users/michaelkaramanov/Desktop/development/dchbx_k8s` | + +## Step-by-Step Procedure + +### Step 1: Validate inputs + +- Confirm the client is `me` or `dc`. If not, report the error and stop. +- Confirm the k8s repo exists at the expected local path. If not, report the error and stop. + +### Step 2: Update the k8s repo (READ-ONLY operations only) + +```bash +cd +git checkout trunk +git pull origin trunk +``` + +These are the ONLY git commands you are allowed to run in the k8s repo. Do NOT run any other git operations. + +### Step 3: Verify the environment exists + +Check that `/environments//config/env-configmap.yaml` exists. If it does not, list the available environments from `/environments/` and report the error. + +### Step 4: Scan enroll client config for referenced ENV variables + +Scan all files under `config/client_config//` in the enroll repo for ENV variable references. The pattern used is: + +``` +ENV['VARIABLE_NAME'] +``` + +Use grep to extract all unique variable names: + +```bash +grep -rhoP "ENV\['([A-Z_0-9]+)'\]" config/client_config// | sed "s/ENV\['\(.*\)'\]/\1/" | sort -u +``` + +This produces the **allowlist** of variable names. Only these variables may appear in the `.env` file. + +### Step 5: Parse k8s configmaps + +Read two files from the k8s repo: + +1. **Base configmap**: `/base/config/env-configmap.yaml` +2. **Environment configmap**: `/environments//config/env-configmap.yaml` + +Both files are Kubernetes ConfigMap YAML. The env vars are flat key-value pairs under the `data:` key: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + ... +data: + KEY_NAME: "value" + ANOTHER_KEY: "value" +``` + +Parse the `data:` section from both files. Environment values override base values (Kustomize strategic merge). + +### Step 6: Build the merged variable map + +1. Start with all key-value pairs from the **base** configmap `data:` section. +2. Overlay all key-value pairs from the **environment** configmap `data:` section (these win on conflict). +3. Filter the merged map to **only** include keys present in the allowlist from Step 4. +4. Remove any keys matching the sensitive value patterns listed in the safety rules above. + +### Step 7: Back up existing .env file + +If a `.env` file exists at the enroll project root, copy it to `.env.bak`: + +```bash +cp .env .env.bak +``` + +Warn the user that the backup was created. + +### Step 8: Write the .env file + +Write the `.env` file at the enroll project root with the following format: + +``` +# Feature flag environment variables +# Source: environments//config/env-configmap.yaml +# Client: +# Generated: +# WARNING: This file was auto-generated. Manual edits will be lost on next fetch. + +CLIENT= +RAILS_ENV=development + +KEY_1=value1 +KEY_2=value2 +... +``` + +Rules for the `.env` file: +- `CLIENT=` must be the first env var (after the header comments). +- `RAILS_ENV=development` must be the second env var. +- Remaining variables should be sorted alphabetically. +- Values should be unquoted unless they contain spaces, `#`, or special characters — in which case use double quotes. +- Boolean values from the k8s configmap (e.g. `"true"`, `"false"`) should be written without quotes: `KEY=true`. + +### Step 9: Report results + +After writing the file, report: +- Total number of variables written (excluding comments and blank lines). +- Number of variables from the allowlist that were **not found** in the k8s configmaps (list them if there are fewer than 20). +- Whether a `.env.bak` backup was created. +- The source environment and repo used. + +## Error Handling + +- If the k8s repo is not found locally, stop and tell the user to clone it. +- If the environment directory does not exist, list available environments and stop. +- If the base or environment configmap file is missing, report which file is missing and stop. +- If no ENV variables are found in the client config scan, report this anomaly and stop. +- If no matching variables are found in the k8s configmaps, report this and stop — do not write an empty `.env`. + +## What NOT to Do + +- Do NOT modify any file in the k8s repo. +- Do NOT stage, commit, or push changes in the k8s repo. +- Do NOT include env vars that are not referenced in `config/client_config//`. +- Do NOT include sensitive values (passwords, keys, certificates, tokens, secrets). +- Do NOT modify any enroll file other than `.env` (and `.env.bak` as a backup). +- Do NOT guess or fabricate values. If a variable is not in the k8s configmap, omit it. +- Do NOT ask the user follow-up questions. Execute the procedure and report results. diff --git a/.claude/skills/bug-fix/SKILL.md b/.claude/skills/bug-fix/SKILL.md index ee3479ea2b9..fb64b31ec15 100644 --- a/.claude/skills/bug-fix/SKILL.md +++ b/.claude/skills/bug-fix/SKILL.md @@ -13,13 +13,25 @@ Implement a bug fix from start to finish using an existing triage report on a Cl ## Workflow -### Step 1 — Get the ClickUp task ID +### Step 1 — Confirm client and environment + +Ask the user: + +> "Which client and environment is this fix targeting? (e.g. `me pvt-3`, `dc prod`, `me preprod`)" + +Once the user provides a client (`me` or `dc`) and environment (e.g. `pvt-3`, `prod`), check whether the current `.env` file at the project root already reflects those values (look for `CLIENT=` as the first line). If it does, inform the user and skip the fetcher. If it does not, invoke the **`feature-flag-fetcher` agent** with the client and environment, e.g.: + +> "Fetch feature flags for me pvt-3" + +This populates the `.env` file with the feature flag settings active in that environment. You will consult these values when reading code to understand which branches and paths are live. Note: the `.env` file sets `RAILS_ENV=development` for local dev — test commands always pass `RAILS_ENV=test` explicitly, which takes precedence. + +### Step 2 — Get the ClickUp task ID If not already provided, ask: > "What is the ClickUp task ID or URL for the bug to fix? (e.g. `#abc123def` or `https://app.clickup.com/t/`)" -### Step 2 — Fetch task context +### Step 3 — Fetch task context Delegate to the `clickup-context-fetcher` agent (`.claude/agents/clickup-context-fetcher.md`) with the task ID. It will return the task description, all comments, and any stack traces. @@ -33,16 +45,18 @@ If no triage comment is found, stop and tell the user: > "No triage report was found on this task. Please run the bug-triage skill first, or paste the triage findings directly into the chat." -### Step 3 — Understand the fix +### Step 4 — Understand the fix + +Before writing any code, read every file listed in the **Affected Components** table extracted in Step 3. Then read any additional context needed to understand the surrounding code (callers, related models, specs for affected methods). -Before writing any code, read every file listed in the **Affected Components** table extracted in Step 2. Then read any additional context needed to understand the surrounding code (callers, related models, specs for affected methods). +When tracing code paths, consult the `.env` file for any feature flag guards (e.g. `EnrollRegistry[:some_feature].enabled?`) to confirm which branches are active in the target environment. Only reason about and fix code paths that are actually live. Ensure that you understand: 1. The exact code change needed 2. Which files will be modified 3. Whether the fix touches any domain operations, event publishers, or background jobs (which have stricter conventions — see relevant skills below) -### Step 4 — Create a branch +### Step 5 — Create a branch ```bash git checkout trunk @@ -52,7 +66,7 @@ git checkout -b CU-/fix/ Use the ClickUp task ID prefix so GitHub auto-links the branch to the task (e.g. `CU-ae27de/fix/enrollment-subscriber-nil-guard`). ClickUp detects `CU-{task_id}` anywhere in a branch name, commit message, or PR title/body. -### Step 5 — Implement the fix +### Step 6 — Implement the fix Apply the minimal change that resolves the root cause. @@ -64,7 +78,7 @@ If the fix touches files in `app/event_source/`, read and follow the [event-sour For all other conventions (frozen string literal, Dry::Monads, i18n, Pundit, `.html_safe`, etc.) refer to `CLAUDE.md` and `AGENTS.md`. -### Step 6 — Lint +### Step 7 — Lint Run RuboCop only on lines changed since `origin/trunk`: @@ -80,24 +94,24 @@ bundle exec rubocop-git -a origin/trunk Resolve any remaining offenses before continuing. -### Step 7 — Run relevant tests +### Step 8 — Run relevant tests -All test commands require `RAILS_ENV=test` and `CLIENT=dc` as environment variables. +All test commands use `RAILS_ENV=test` and `CLIENT=dc` — the CI/GHA suite always runs against `dc`, regardless of which client the bug affects. -**7a.** Run existing specs for affected files: +**8a.** Run existing specs for affected files: ```bash RAILS_ENV=test CLIENT=dc bundle exec rspec spec/path/to/relevant_spec.rb ``` -**7b.** If the bug had no prior test coverage, write a new spec: +**8b.** If the bug had no prior test coverage, write a new spec: 1. Write a targeted RSpec example that reproduces the bug 2. Confirm it fails on the pre-fix code (if verifiable) 3. Confirm it passes after the fix 4. Place it in the appropriate spec file — prefer unit tests over integration tests -**7c.** If the fix changes UI behavior, also run the relevant Cucumber feature: +**8c.** If the fix changes UI behavior, also run the relevant Cucumber feature: ```bash RAILS_ENV=test CLIENT=dc bundle exec cucumber features/path/to/relevant.feature @@ -105,7 +119,7 @@ RAILS_ENV=test CLIENT=dc bundle exec cucumber features/path/to/relevant.feature Fix any test failures before proceeding. -### Step 8 — Commit +### Step 9 — Commit ```bash git add @@ -118,7 +132,7 @@ Commit message rules (same as PR title conventions): - No ticket numbers, no punctuation at end - Prefix with `fix:` for bug fixes -### Step 9 — Open a PR +### Step 10 — Open a PR Read `.github/PULL_REQUEST_TEMPLATE.md` and complete every section based on the fix you implemented. Fill in all checkboxes that apply and leave inapplicable sections (e.g. Feature Flag) blank. @@ -142,7 +156,7 @@ gh pr create \ Note the PR URL returned. -### Step 10 — Update ClickUp +### Step 11 — Update ClickUp Post a comment on the task using `clickup_create_task_comment`: @@ -167,4 +181,5 @@ Then update the task status to `Peer Review` using `clickup_update_task`. - **Minimal diff**: fix only what the triage identifies; do not refactor unrelated code - **Never disable RuboCop cops** without a documented reason in the same file +- **Always use `CLIENT=dc` for tests** — this matches the deployed CI/GHA test suite, regardless of the target client - **Confirm with the user before posting to ClickUp or opening the PR** if any of the following occurred during implementation: the root cause differed from what the triage identified; files outside the Affected Components list required changes; tests revealed a broader regression than the reported bug; or the suggested remediation was incorrect and you took a materially different approach diff --git a/.claude/skills/bug-triage/SKILL.md b/.claude/skills/bug-triage/SKILL.md index e0ffecefa98..d188d4f37cb 100644 --- a/.claude/skills/bug-triage/SKILL.md +++ b/.claude/skills/bug-triage/SKILL.md @@ -13,7 +13,19 @@ Perform read-only technical investigation of a bug ticket. The goal is to identi ## Workflow -### Step 1 — Get the ClickUp task number +### Step 1 — Confirm client and environment + +Ask the user: + +> "Which client and environment does this bug affect? (e.g. `me pvt-3`, `dc prod`, `me preprod`)" + +Once the user provides a client (`me` or `dc`) and environment (e.g. `pvt-3`, `prod`), check whether the current `.env` file at the project root already reflects those values (look for `CLIENT=` as the first line). If it does, inform the user and skip the fetcher. If it does not, invoke the **`feature-flag-fetcher` agent** with the client and environment, e.g.: + +> "Fetch feature flags for me pvt-3" + +This populates the `.env` file with the feature flag settings active in that environment. You will consult these values throughout code research to understand which code paths are live. + +### Step 2 — Get the ClickUp task number If the user has not already provided one, ask: @@ -21,15 +33,15 @@ If the user has not already provided one, ask: ClickUp task IDs appear in the URL as `https://app.clickup.com/t/`. -### Step 2 — Fetch task context +### Step 3 — Fetch task context -Use the **Agent tool** with `subagent_type=clickup-context-fetcher`, passing the task ID. The agent will call `clickup_get_task` and `clickup_get_task_comments` and return a structured summary. Use that summary as the basis for your research in Step 3. +Use the **Agent tool** with `subagent_type=clickup-context-fetcher`, passing the task ID. The agent will call `clickup_get_task` and `clickup_get_task_comments` and return a structured summary. Use that summary as the basis for your research in Step 4. If the Agent tool returns an error indicating the ClickUp MCP server is not configured, tell the user: > "The ClickUp MCP server doesn't appear to be set up. Please paste the full ticket context into the chat — description, reproduction steps, comments, stack traces, anything relevant — and I'll proceed from there." -### Step 3 — Research the codebase +### Step 4 — Research the codebase Using only read tools (Grep, Glob, Read), investigate the codebase. Do NOT use Shell to run the app or tests. @@ -38,15 +50,16 @@ Focus your research on: 1. **Locate the entry point** — find the controller action, background job, event subscriber, or operation triggered by the user action described in the bug 2. **Trace the code path** — follow execution from the entry point through services, domain operations, models, and any external integrations 3. **Identify the failure point** — find the specific line(s) or logic where the bug likely originates -4. **Assess the scope of impact** — identify all other parts of the system that could be affected: +4. **Account for feature flags** — whenever you encounter a feature flag guard (e.g. `EnrollRegistry[:some_feature].enabled?`), check the `.env` file to determine whether that flag is enabled in the target environment. Only trace code paths that are actually active. Note explicitly in your findings when a feature flag is relevant to the bug. +5. **Assess the scope of impact** — identify all other parts of the system that could be affected: - Other callers of the failing method/class - Related models, validations, or callbacks - Event publishers/subscribers that may be triggered - Background jobs that depend on affected data - Views or API responses that surface the broken data -5. **Check for tests** — note whether the affected code has RSpec or Cucumber coverage +6. **Check for tests** — note whether the affected code has RSpec or Cucumber coverage -### Step 4 — Present findings for review +### Step 5 — Present findings for review Read [TRIAGE_TEMPLATE.md](TRIAGE_TEMPLATE.md) and use it to format the full triage summary. Present it to the user, then ask: @@ -54,7 +67,7 @@ Read [TRIAGE_TEMPLATE.md](TRIAGE_TEMPLATE.md) and use it to format the full tria Do not post to ClickUp until the user confirms. -### Step 5 — Post to ClickUp +### Step 6 — Post to ClickUp Once the user confirms, use `create_comment` on the task to post the formatted summary. @@ -67,5 +80,5 @@ Once the user confirms, use `create_comment` on the task to post the formatted s - **No fixes**: do not implement any code changes - **Always confirm** before posting to ClickUp - **Cite specific file paths** in your findings — never be vague about location -- **Only read TRIAGE_TEMPLATE.md when preparing the Step 4 summary** — not before +- **Only read TRIAGE_TEMPLATE.md when preparing the Step 5 summary** — not before - Use the [understanding-the-project skill](../understanding-the-project/SKILL.md) if you need orientation on architecture (engines, domain operations, event source, etc.) diff --git a/.claude/skills/create-plan/SKILL.md b/.claude/skills/create-plan/SKILL.md index f27693d3ec1..31827c29f89 100644 --- a/.claude/skills/create-plan/SKILL.md +++ b/.claude/skills/create-plan/SKILL.md @@ -33,7 +33,19 @@ Then wait for the user's input. ## Process Steps -### Step 1: Context Gathering & Initial Analysis +### Step 1: Confirm client and environment + +Ask the user: + +> "Which client and environment is this plan targeting? (e.g. `me pvt-3`, `dc prod`, `me preprod`)" + +Once the user provides a client (`me` or `dc`) and environment, check whether the current `.env` file at the project root already reflects those values (look for `CLIENT=` as the first line). If it does, inform the user and skip the fetcher. If it does not, invoke the **`feature-flag-fetcher` agent** with the client and environment, e.g.: + +> "Fetch feature flags for me pvt-3" + +This populates the `.env` with the feature flag settings active in that environment. Consult the `.env` throughout research and planning when reasoning about which code paths and conditional branches are live — plans must reflect what is actually enabled, not hypothetical configurations. + +### Step 2: Context Gathering & Initial Analysis 1. **Read all mentioned files immediately and FULLY**: - Any files the user references @@ -239,8 +251,8 @@ After structure approval: #### Automated Verification: - [ ] RuboCop passes: `bundle exec rubocop path/to/changed/files` -- [ ] RSpec passes: `RAILS_ENV=test CLIENT=me bundle exec rspec spec/path/to/file_spec.rb` -- [ ] Cucumber passes (if UI changed): `RAILS_ENV=test CLIENT=me bundle exec cucumber features/path/to/file.feature` +- [ ] RSpec passes: `RAILS_ENV=test CLIENT=dc bundle exec rspec spec/path/to/file_spec.rb` +- [ ] Cucumber passes (if UI changed): `RAILS_ENV=test CLIENT=dc bundle exec cucumber features/path/to/file.feature` #### Manual Verification: - [ ] [Specific UI or functional check] diff --git a/.claude/skills/implement-plan/SKILL.md b/.claude/skills/implement-plan/SKILL.md index 48c3ce7e229..80c666f4f82 100644 --- a/.claude/skills/implement-plan/SKILL.md +++ b/.claude/skills/implement-plan/SKILL.md @@ -55,15 +55,15 @@ bundle exec rubocop path/to/changed/files **RSpec** (for unit/integration tests): ```bash -RAILS_ENV=test CLIENT=me bundle exec rspec spec/path/to/file_spec.rb +RAILS_ENV=test CLIENT=dc bundle exec rspec spec/path/to/file_spec.rb ``` **Cucumber** (required if any views or UI flows were changed): ```bash -RAILS_ENV=test CLIENT=me bundle exec cucumber features/path/to/file.feature +RAILS_ENV=test CLIENT=dc bundle exec cucumber features/path/to/file.feature ``` -> **Note**: `CLIENT` and `RAILS_ENV` are required for all test commands. Use `RAILS_ENV=test` for rspec/cucumber, `RAILS_ENV=development` for rake tasks or console. +> **Note**: `CLIENT` and `RAILS_ENV` are required for all test commands. Always use `CLIENT=dc` for rspec/cucumber — this matches the CI/GHA test suite regardless of target client. Use the relevant client (`me` or `dc`) for rake tasks, console, or server. Before running tests, confirm the `.env` at the project root reflects the target client and environment noted in the plan (the `feature-flag-fetcher` agent populates this). After all automated checks pass: - Update checkboxes in the plan file for completed automated items diff --git a/.claude/skills/research-codebase/SKILL.md b/.claude/skills/research-codebase/SKILL.md index 5a6d7ae3505..fdabe9c2e85 100644 --- a/.claude/skills/research-codebase/SKILL.md +++ b/.claude/skills/research-codebase/SKILL.md @@ -26,6 +26,14 @@ I'm ready to research the codebase. Please provide your research question or are Then wait for the user's research query. +## After receiving the research query — check feature flag relevance + +Before spawning any research agents, consider whether the research topic involves feature-flag-gated behavior (e.g. the user is asking about a specific feature, a code path that may be conditionally enabled, or a bug in a particular environment). If so, ask: + +> "Is this research targeted at a specific client and environment (e.g. `me pvt-3`, `dc prod`)? If yes, I can sync the feature flag settings from that environment into `.env` first so the research reflects what's actually live." + +If the user provides a client and environment, check whether the current `.env` already reflects those values. If not, invoke the **`feature-flag-fetcher` agent** before proceeding. If the research is general/architectural and not environment-specific, skip this and proceed directly. + ## Steps to follow after receiving the research query: 1. **Read any directly mentioned files first:** diff --git a/CLAUDE.md b/CLAUDE.md index f04a61fc8b1..eb9086cfc6a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,10 @@ Enroll is an open-source health benefit exchange platform. It's a Rails app usin ## Common Commands -All `bundle exec` commands require `CLIENT` and `RAILS_ENV` env vars. Use `RAILS_ENV=test` for rspec/cucumber, `RAILS_ENV=development` for server/console/rake. Example: `RAILS_ENV=test CLIENT=me bundle exec rspec spec/path/to/file_spec.rb` +All `bundle exec` commands require `CLIENT` and `RAILS_ENV` env vars: + +- **Tests** (rspec/cucumber): always use `CLIENT=dc` regardless of target client — this matches the CI/GHA test suite. Example: `RAILS_ENV=test CLIENT=dc bundle exec rspec spec/path/to/file_spec.rb` +- **Everything else** (server, console, rake, migrations): use the relevant client (`me` or `dc`). Example: `RAILS_ENV=development CLIENT=me bundle exec rails console` ## Architecture