* fix(featurable): backport feature_flag_value helper from chatwoot-pro-main Adds the two's-complement-aware helper that returns a signed bigint-safe value for SQL queries against the feature_flags column. Mirrors the existing helper in chatwoot-pro-main so future backports of pro features that reference it (e.g. kanban filters) compile cleanly on main. Note: the helper does NOT fix FlagShihTzu's write path; new account-level toggles should use account.settings jsonb instead of feature_flags (see AGENTS.md). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(super-admin): toggle to hide assignee tabs for basic agents Adds two account-level settings, configurable from the super admin dashboard, that hide the "Unassigned" and "All" tabs of the conversation list for users with the basic agent role (admins and custom roles are unaffected). Hiding "Unassigned" implicitly hides "All", since seeing the full queue without the unassigned subset is incoherent. The constraint is enforced both in the backend (before_validation forces hide_agent_all_tab=true when hide_agent_unassigned_tab is on) and in the super admin form (the "All" checkbox is disabled and auto-checked when "Unassigned" is checked). Storage uses account.settings (jsonb) instead of feature_flags to sidestep the bigint bit-position overflow that happens once features.yml crosses 64 entries, and to keep keys stable across the main and chatwoot-pro-main forks where feature bit positions diverge. AGENTS.md documents the rationale and the recipe to add future toggles. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(chat-list): guard activeAssigneeTabCount against missing tab When the visibility settings hide the currently selected tab, the fallback watch resets activeAssigneeTab to ME, but activeAssigneeTabCount re-evaluates in the same reactive cycle and can read .count on undefined before the watch flushes. Use optional chaining + nullish fallback so the count safely returns 0 during the brief inconsistency. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.6 KiB
8.6 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
rvm - 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
- MVP focus: Least code change, happy-path only
- No unnecessary defensive programming
- Ship the happy path first: limit guards/fallbacks to what production has proven necessary, then iterate
- 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
- 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.
Release Notes
- Every GitHub release cut from this repo must include the bilingual
user-notesblocks (pt-BR + en) in the release body, written for non-technical end users. - Before running
gh release create,gh release edit, thereleaseskill fromfazer-ai-tools, or any flow that touches a release body (including retroactive backfills), invoke therelease-notesskill at.claude/skills/release-notes/SKILL.mdto draft and validate the blocks.
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:
- Update
en.yml/en.jsonandpt_BR.yml/pt_BR.json - Other languages are handled by the community
- Backend i18n →
.yml, Frontend i18n →.json
- 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.
Account-level toggles: do NOT extend config/features.yml
Account#feature_flagsis abigintdriven by FlagShihTzu, with each YAML entry mapped to bit positionindex(0-based). Signed bigint can only hold bits 0..63. Adding a 65th entry produces values >= 2^64 that overflow the column on write and silently break high-bit features.chatwoot-pro-mainalready insertskanbanandinternal_chat_promid-list, pushing upstream features to bits 60+. After merging into Pro, any new flag added onmainlands at an even higher bit, accelerating the overflow. TheFeaturable.feature_flag_valuehelper applies a two's-complement workaround that only fixes manual SQL queries (feature_flags & ? != 0); it does NOT fix the FlagShihTzu write path used by the superadmin form.- Local DB pitfall: bit positions differ between
mainandchatwoot-pro-mainbecause of the kanban/internal_chat_pro insertion. The same bit set on one branch maps to a different feature on the other. Use separate dev DBs per branch or resetfeature_flagswhen switching.
For NEW account-level toggles, prefer the settings jsonb column instead of feature_flags:
- Declare a
store_accessor :settings, :your_toggleinapp/models/account.rband override the writer to cast (super(ActiveModel::Type::Boolean.new.cast(value))for booleans) so JSON schema validation accepts the value. - Add the key to
SETTINGS_PARAMS_SCHEMAinapp/models/concerns/account_settings_schema.rb. - Register it as a
Field::Boolean(or appropriate field) inapp/dashboards/account_dashboard.rb(ATTRIBUTE_TYPES,FORM_ATTRIBUTES,SHOW_PAGE_ATTRIBUTES). - The frontend reads it from
account.settings.your_toggle(already serialized viaapp/views/api/v1/models/_account.json.jbuilderasjson.settings resource.settings).
This keeps toggles keyed by name (immune to bit-position drift between branches) and unbounded by the bigint width.