## Summary Adds the minimal foundation for Cloud marketing conversion tracking: - locked internal installation config for conversion tracking credentials and event mappings - generic background job and service for uploading conversion events from stored attribution - focused coverage for Cloud gating, click-id selection, payload shape, and optional conversion value This PR intentionally does not wire signup or plan activation yet. The next step is to validate the service from Rails console against existing attributed accounts, then add the event hooks in a follow-up PR. ## Notes The config remains locked and is surfaced under the Internal settings group next to the existing Cloud plan configuration. The service assumes the locked config is present and valid; config mistakes should surface instead of being silently ignored.
7.4 KiB
7.4 KiB
Chatwoot Development Guidelines
Build / Test / Lint
- Setup:
bundle install && pnpm install - Run Dev:
pnpm devorovermind start -f ./Procfile.dev - Seed Local Test Data:
bundle exec rails db:seed(quickly populates minimal data for standard feature verification) - Seed Search Test Data:
bundle exec rails search:setup_test_data(bulk fixture generation for search/performance/manual load scenarios) - Seed Account Sample Data (richer test data):
Seeders::AccountSeederis available as an internal utility and is exposed through Super AdminAccounts#seed, but can be used directly in dev workflows too:- UI path: Super Admin → Accounts → Seed (enqueues
Internal::SeedAccountJob). - CLI path:
bundle exec rails runner "Internal::SeedAccountJob.perform_now(Account.find(<id>))"(or callSeeders::AccountSeeder.new(account: Account.find(<id>)).perform!directly).
- UI path: Super Admin → Accounts → Seed (enqueues
- Lint JS/Vue:
pnpm eslint/pnpm eslint:fix - Lint Ruby:
bundle exec rubocop -a - Test JS:
pnpm testorpnpm test:watch - Test Ruby:
bundle exec rspec spec/path/to/file_spec.rb - Single Test:
bundle exec rspec spec/path/to/file_spec.rb:LINE_NUMBER - Run Project:
overmind start -f Procfile.dev - Ruby Version: Manage Ruby via
rbenvand install the version listed in.ruby-version(e.g.,rbenv install $(cat .ruby-version)) - rbenv setup: Before running any
bundleorrspeccommands, init rbenv in your shell (eval "$(rbenv init -)") so the correct Ruby/Bundler versions are used - Always prefer
bundle execfor Ruby CLI tasks (rspec, rake, rubocop, etc.)
Code Style
- Ruby: Follow RuboCop rules (150 character max line length)
- Vue/JS: Use ESLint (Airbnb base + Vue 3 recommended)
- Vue Components: Use PascalCase
- Events: Use camelCase
- I18n: No bare strings in templates; use i18n
- Error Handling: Use custom exceptions (
lib/custom_exceptions/) - Models: Validate presence/uniqueness, add proper indexes
- Type Safety: Use PropTypes in Vue, strong params in Rails
- Naming: Use clear, descriptive names with consistent casing
- Vue API: Always use Composition API with
<script setup>at the top
Styling
- Tailwind Only:
- Do not write custom CSS
- Do not use scoped CSS
- Do not use inline styles
- Always use Tailwind utility classes
- Colors: Refer to
tailwind.config.jsfor color definitions
General Guidelines
- Prefer the smallest production-ready change that solves the current problem.
- Build for the expected production path first. Do not add speculative guards, fallbacks, retries, or edge-case handling unless the caller can actually hit that case or production has proven it necessary.
- When an impossible or misconfigured state would indicate a setup/deployment bug, let it fail loudly instead of silently skipping behavior.
- For locked/internal configs that must exist in production, prefer direct reads (
find,find_by!, required hash keys) over silent fallbacks. - Do not add validation or response checks unless the code uses the result or the check changes behavior meaningfully.
- Prefer existing repo dependencies/client libraries over hand-rolled protocol code for auth, signing, parsing, or API plumbing.
- Avoid one-use private helpers unless they hide real complexity or make the main flow meaningfully easier to read.
- Prefer minimal, readable code over elaborate abstractions; clarity beats cleverness
- Break down complex tasks into small, testable units
- Iterate after confirmation
- Avoid writing specs unless explicitly asked
- In specs, avoid custom helper methods for setup/data. Prefer
letvalues and direct per-example setup; only add a helper when it removes meaningful repeated complexity. - Remove dead/unreachable/unused code
- Don’t write multiple versions or backups for the same logic — pick the best approach and implement it
- Prefer
with_modified_env(from spec helpers) over stubbingENVdirectly in specs - Specs in parallel/reloading environments: prefer comparing
error.class.nameover constant class equality when asserting raised errors
Codex Worktree Workflow
- Use a separate git worktree + branch per task to keep changes isolated.
- Keep Codex-specific local setup under
.codex/and useProcfile.worktreefor worktree process orchestration. - The setup workflow in
.codex/environments/environment.tomlshould dynamically generate per-worktree DB/port values (Rails, Vite, Redis DB index) to avoid collisions. - Start each worktree with its own Overmind socket/title so multiple instances can run at the same time.
Commit Messages
- Prefer Conventional Commits:
type(scope): subject(scope optional) - Example:
feat(auth): add user authentication - Don't reference Claude in commit messages
PR Description Format
- Start with a short, user-facing paragraph describing the product change.
- Add a
Closessection with relevant issue links (GitHub, Linear, etc.). - For feature PRs, add
How to testfrom a product/UX standpoint. - For bugfix PRs, use
How to reproducewhen helpful. - Optionally add a
What changedsection for implementation highlights. - Do not add a
How this was testedsection listing specs/commands.
Project-Specific
- Translations:
- Only update
en.ymlanden.json - Other languages are handled by the community
- Backend i18n →
en.yml, Frontend i18n →en.json
- Only update
- Frontend:
- Use
components-next/for message bubbles (the rest is being deprecated)
- Use
Ruby Best Practices
- Use compact
module/classdefinitions; avoid nested styles
Enterprise Edition Notes
- Chatwoot has an Enterprise overlay under
enterprise/that extends/overrides OSS code. - When you add or modify core functionality, always check for corresponding files in
enterprise/and keep behavior compatible. - Follow the Enterprise development practices documented here:
Practical checklist for any change impacting core logic or public APIs
- Search for related files in both trees before editing (e.g.,
rg -n "FooService|ControllerName|ModelName" app enterprise). - If adding new endpoints, services, or models, consider whether Enterprise needs:
- An override (e.g.,
enterprise/app/...), or - An extension point (e.g.,
prepend_mod_with, hooks, configuration) to avoid hard forks.
- An override (e.g.,
- Avoid hardcoding instance- or plan-specific behavior in OSS; prefer configuration, feature flags, or extension points consumed by Enterprise.
- Keep request/response contracts stable across OSS and Enterprise; update both sets of routes/controllers when introducing new APIs.
- When renaming/moving shared code, mirror the change in
enterprise/to prevent drift. - Tests: Add Enterprise-specific specs under
spec/enterprise, mirroring OSS spec layout where applicable. - When modifying existing OSS features for Enterprise-only behavior, add an Enterprise module (via
prepend_mod_with/include_mod_with) instead of editing OSS files directly—especially for policies, controllers, and services. For Enterprise-exclusive features, place code directly underenterprise/.
Branding / White-labeling note
- For user-facing strings that currently contain "Chatwoot" but should adapt to branded/self-hosted installs, prefer applying
replaceInstallationNamefromshared/composables/useBrandingin the UI layer (for example tooltip and suggestion labels) instead of adding hardcoded brand-specific copy.