BizBotify Test Docs

Enter the password to access test documentation.

PR #109: fix: always show all tags in contact filter dropdown
2026-06-12 11:22:21 UTC
campaigns
Test cases updated.
contacts
Test cases updated.
PR #107: Org default WhatsApp account and settings ID disclosure
2026-06-12 10:20:04 UTC
auth
Test cases updated.
campaigns
Test cases updated.
settings
Test cases updated.
PR #106: Analytics UX Phase 1: trust, coherence, and actionability
2026-06-12 07:41:48 UTC
analytics
Test cases updated.
PR #104: Campaign live updates via WebSocket
2026-06-12 04:38:42 UTC
campaigns
Test cases updated.
PR #100: Integration hub Phase 2: message send + media upload API
2026-06-11 06:06:33 UTC
campaigns
Test cases updated.
PR #99: fix: resolve verification redirect stuck issue on OTP completion
2026-06-10 07:30:00 UTC
backend
No behavior changes.
ui
Added watch(step) to VerifyView.vue that immediately and reactively redirects fully verified users to /chat. Removed redundant redirect from onMounted.
tests
Created VerifyView.spec.ts unit tests covering mount and dynamic state transitions. All vitest (416) and pytest (1485) suites pass successfully.
PR #97: CRM integration hub: contact sync API + Settings connections UI
2026-06-10 04:17:31 UTC
campaigns
Test cases updated.
PR #94: Show campaign recipients in inbox ( Campaign status)
2026-06-09 08:02:32 UTC
campaigns
Test cases updated.
PR #91: Unify email and phone verification UX
2026-06-08 11:22:05 UTC
auth
Test cases updated.
PR #89: Feature/whatsapp template new pages
2026-06-08 03:22:53 UTC
auth
Test cases updated.
templates
Test cases updated.
PR #86: Fix autopilot duplicate sends when frequency cap is enabled
2026-06-05 11:26:10 UTC
campaigns
Test cases updated.
PR #84: feat(contacts): customer self-service marketing opt-out (STOP/START)
2026-06-03 12:10:34 UTC
auth
Test cases updated.
campaigns
Test cases updated.
contacts
Test cases updated.
PR #83: fix delete conflict handling for whatsapp accounts and follow-ups
2026-06-02 11:17:07 UTC
campaigns
Test cases updated.
settings
PR #83 adds conflict handling for WhatsApp account deletion. Three new test cases (SET-108, SET-109, SET-110, SET-111) cover scenarios where accounts with foreign key dependencies (nurture sequences, campaigns, follow-ups) cannot be deleted, returning a 409 status with user-friendly error messaging and structured logging.
PR #82: feat(campaigns): contact audit trail for campaign lifecycle events
2026-06-02 05:38:24 UTC
campaigns
Test cases updated.
PR #78: refactor(campaigns): extract BroadcastsView + shared messaging primitives
2026-05-30 05:12:50 UTC
campaigns
Test cases updated.
PR #77: feat(campaigns): split CampaignsView monolith into child-route architecture
2026-05-30 04:00:00 UTC
campaigns
Replaced the 2700-line CampaignsView.vue monolith with a clean child-route architecture. Four new files created: CampaignsLayout.vue (thin shell: page title + 3-tab nav + RouterView), BroadcastsView.vue (all broadcast CRUD, 4-step creation wizard, campaign detail/polling), SequencesView.vue (AutoFollowUpWizard + NurtureSequenceTab), WaTemplatesView.vue (thin wrapper around WhatsAppTemplatesTab). Each tab now has its own dedicated URL (/campaigns/broadcasts, /campaigns/sequences, /campaigns/templates) and is independently lazy-loaded. A beforeEnter guard on the parent /campaigns route handles backward-compat: redirects legacy ?tab=sequences → /campaigns/sequences and ?tab=wa-templates → /campaigns/templates; bare /campaigns redirects to /campaigns/broadcasts. The /templates shortcut redirect updated to /campaigns/templates (was ?tab=wa-templates). AddToFollowUpModal's router.push updated from { name: 'campaigns' } to { name: 'campaigns-sequences' }. CampaignsView.spec.ts migrated to views/campaigns/BroadcastsView.spec.ts with child-route mount setup using createMemoryHistory; tabs are tested via router.push(path) not ?tab=. auth-navigation.spec.ts assertion updated: /campaigns resolves to route name campaigns-broadcasts. test/helpers.ts stubCampaignsViewApis gained a /contacts/fields stub. All 261 tests pass; build clean (no TypeScript errors).
routing
Tab navigation for /campaigns module is now URL-driven (child routes) instead of query-param driven. Deep links, browser back/forward, and copy-paste URLs all work correctly for each campaigns tab.
PR #76: feat(campaigns): Auto follow-up wizard and nurture v2
2026-05-29 12:00:00 UTC
auto-follow-ups
Expanded manual test matrix in docs/manual-tests/src/auto-follow-ups.md to 80+ cases (AFU-001–AFU-202): wizard triggers, contact-first manual enroll, detail page, worker/runtime, reenroll/cooldown, security, migration 0075, and review-hardening cases (worker skip, status load errors, API dedupe). Includes environment setup, smoke checklist, and defect template.
PR #76: feat(nurture): redesign auto follow-up details page with premium Apple-style layout
2026-05-29 09:28:00 UTC
campaigns
Redesigned the auto follow-up details view in NurtureSequenceTab.vue. Implemented an integrated controls capsule, dynamic campaign engine status card, premium tinted metrics grid, a Configuration Blueprint panel, and a spacious Audience Ledger table. Fixed related frontend TypeScript compile errors and ensured all unit tests pass cleanly.
PR #76: feat(nurture): simplify auto follow-up wizard steps 3, 4 & 5 layout and UX
2026-05-29 14:35:00 UTC
campaigns
Simplified Step 3 sequence timeline in AutoFollowUpWizard.vue to use an evenly-spaced, compact horizontal flex-wrap steps row of clickable step cards. Redesigned Step 4 (Audience & Safety) to use tactile card layouts, interactive re-enrollment settings, custom exit rules, and comma-separated tag exclusions. Redesigned Step 5 (Review & Launch) as a launch console featuring grouped summary cards (Identity & Sender, Trigger & Safety), a dedicated drip sequence card, and an interactive self-test sandbox. Refactored preflight check banners and streamlined footer CTA actions to 'Save Draft' and 'Activate Follow-up'. Updated unit tests in AutoFollowUpWizard.spec.ts.
PR #76: feat(campaigns): Auto follow-up detail page delivery metrics and journey drawer
2026-05-29 12:40:00 UTC
campaigns
Auto follow-up detail page now mirrors campaign reporting: Sent/Delivered/Read/Failed KPI cards, execution banners (paused vs on-schedule), collapsible setup summary, paginated people table with enrollment and last-message status filters, journey stepper dots, and relative timestamps. Backend: extended GET /nurture-sequences/{id}/stats with message aggregates and per-step stats; enrollments API returns paginated items with last_sent_at, last_message_status, step_statuses; new GET .../enrollments/{id} for slide-over step log. Nurture worker stores enrollment_id and step_index on message automation_metadata. Frontend: FollowUpEnrollmentDrawer (Open in inbox), FollowUpJourneyStepper, followUpDetail utils. Tests: test_nurture_enrollment_metrics.py, followUpDetail.spec.ts, FollowUpTimelinePreview step stats.
PR #76: feat(nurture): redesign audience rules modal with Apple-style segmented and pill inputs
2026-05-29 10:35:00 UTC
campaigns
Redesigned the Audience Rules builder modal in AutoFollowUpWizard.vue to align with the Apple design spec. Replaced the standard AND/OR conditions operator selector with a premium sliding segmented tab controller ('All conditions' / 'Any conditions'). Styled conditions rows and their operator/value selector menus as interactive, rounded-full pill capsules with soft focus states. Added zero-dependency autocomplete tag suggestions using native HTML5 datalist popover bound to tags input. Restructured conversation inactivity rule exclusions into a dedicated, clean settings card with custom description text and inline values. Added detailed condition preview pills on the Step 2 Trigger screen Segment card for instant visibility. Replaced static wizard sheet headers with dynamic, step-dependent titles (e.g. 1. Setup follow-up details, 2. Configure trigger rules) and matching descriptions to improve contextual progressive guidance. Cleaned up top spacing, divider borders, and extra blank lines inside each step layout now that titles are globally header-driven.
PR #76: feat(nurture): simplify auto follow-up wizard step 1 UI
2026-05-29 04:52:00 UTC
campaigns
Redesigned Step 1 ('Setup & Trigger') in AutoFollowUpWizard.vue using progressive disclosure and a premium Apple-inspired layout. Replaced the 5-column trigger card grid with a compact row of pill button tabs. Moved the segment rule builder out of Step 1 into a gorgeous slide-over overlay drawer. Added a Segment summary card to the main Setup step showing rule counts dynamically.
PR #76: feat(nurture): implement re-enrollment policy settings and enforcement
2026-05-29 09:30:00 UTC
campaigns
Implemented Re-enrollment Policy (allow_reenroll) for auto follow-up sequences. Added allow_reenroll column to database via migration 0073. Enforced policy checks in nurture_service.enroll_lead and enroll_contact: when allow_reenroll is false, checks for any historic completed/cancelled/any enrollment matching the contact's ID and blocks duplicate enrollment. Exposed setting as a premium checkbox toggle in Step 2 of AutoFollowUpWizard.vue.
PR #76: feat(nurture): redesign auto follow-up wizard messages step to use timeline sidebar
2026-05-28 20:34:00 UTC
campaigns
Redesigned Step 3 'Messages' layout in AutoFollowUpWizard.vue to use a 3-column Sequence Timeline Sidebar (Concept 1). Replaced the low-profile horizontal tab pills with a vertical timeline sidebar track (3 cols) on the left featuring custom completion Check dots and step detail cards. Maintained active step editing in the middle column (5 cols) and live WhatsApp mockup previews in the right column (4 cols). Added a 'Configure Next Message' guide button at the bottom of the editor to navigate steps sequentially. Removed the misleading 'BizBotify Nurture' metadata header from the inner preview chat bubble so it only renders actual message content sent to customers. Removed the 'Never send more than X messages' stop rule option from Step 2 of the wizard UI, defaulting it to 20 to prevent premature sequence halts.
PR #76: feat(nurture): redesign auto follow-up wizard trigger step UI & UX
2026-05-28 20:18:00 UTC
campaigns
Redesigned Step 2 'Starts when' layout in AutoFollowUpWizard.vue: changed the trigger card selection into a compact, premium horizontal layout arranged in a 3-column grid featuring inline Lucide icons (GitPullRequest, Tag, Flame, Filter, UserPlus), title text, inline Checkmarks, and descriptions to optimize vertical screen footprint. Styled the tag trigger selection list as pill-shaped interactive badges with select toggles and Check indicators. Updated the segment rules builder conditions to render as clean, white cards with modern borders and subtle shadow styling. Dynamically hide and disable the 'Stop when lead is Converted or Lost' terminal stage option when the tag-based trigger is active.
PR #76: feat(nurture): align auto follow-up variable mapping UI & UX with campaigns
2026-05-28 14:25:00 UTC
campaigns
Aligned Step 3 parameter mapping UI in AutoFollowUpWizard to use structured variable mapping options: contact fields, custom fields, and fixed values. Added automatic watcher to normalize legacy string parameters and presets into the new structured schema. Enhanced step 3 validation checks in wizard. Updated backend Pydantic schema and nurture worker to resolve structured mapping parameters at execution time, falling back to legacy strings when needed. Added a premium side-by-side Live WhatsApp Preview Mockup for the currently highlighted/selected follow-up message step.
PR #76: fix(nurture): lead-less contact-native enrollments database migration and index uniqueness
2026-05-28 11:40:00 UTC
campaigns
Fixed lead_id nullability in migration 0072 to prevent database NOT NULL constraint violation for contact-native enrollments. Added database unique constraint on (sequence_id, contact_id) to prevent duplicate active enrollments for the same contact. Optimized enroll_lead database queries to reuse fetched lead row.
auto-follow-ups
Ensured contact-native follow-up sequences can be successfully created and run without throwing constraint errors. Guaranteed single-active enrollment restriction for contacts.
PR #76: feat(nurture): lead_temperature trigger + audience filter segment trigger
2026-05-27 06:30:00 UTC
campaigns
Extended auto follow-up trigger types. Migration 0071 adds trigger_temperature (VARCHAR) and trigger_segment_rules (JSONB, GIN index) to nurture_sequences.
auto-follow-ups
Step 2 wizard has two new trigger cards: 'When lead is marked Hot or Cold' (real-time, mirrors lead_stage hook) and 'Audience filter' (segment builder — tag/stage/temperature/custom-field conditions with AND/OR, snapshot/backfill-on-activate). Frontend payload forwards trigger_temperature and trigger_segment_rules. audience-preview and preflight endpoints updated. Manual cases: AFU-013, AFU-014, AFU-064, AFU-065.
leads
update_lead() temperature-change block now calls auto_enroll_matching_sequences (same pattern as update_lead_stage). _sequence_matches_lead() checks trigger_temperature; segment trigger skips real-time enroll (backfill only).
PR #76: feat(campaigns): Auto follow-up wizard and nurture v2
2026-05-26 12:00:00 UTC
campaigns
Auto follow-ups (drip/nurture): migration 0069/0070 (trigger_type, stop_rules, audience_rules, stopped_reason); extended nurture_service, worker (skip+advance exclusions, inactive pause), backfill arq job; nurture API with campaigns:read/write, preflight, audience-preview; stop-on-reply webhook hook; 5-step AutoFollowUpWizard + list/detail UI; POST /campaigns/test-template; manual matrix AFU-001+ in docs/manual-tests/src/auto-follow-ups.md.
auto-follow-ups
See campaigns summary. High manual: AFU-010, 051, 052, 060, 061.
leads
Enrollment API returns progress_label; stage change auto-enroll; terminal stage stop rules.
PR #75: fix(campaigns): fix continuous and paced campaigns premature completion
2026-05-26 06:56:46 UTC
campaigns
Test cases updated.
PR #75: feat(campaigns): enable campaign limit resets in minutes in staging and dev
2026-05-26 04:52:00 UTC
campaigns
Enabled campaign daily limits (daily_limit) reset in minutes instead of days in non-production environments (development and staging) using a new helper function get_next_daily_sent_reset_at. Added new environment-level configuration setting DEV_PACING_INTERVAL_MINUTES (defaulting to 1 minute) to allow customized testing reset intervals. Configured arq worker cron schedule to run run_smart_campaigns every minute (second={0}) in non-production environments to align evaluation/run frequency with the minute-level limits. Added extensive unit tests for helper reset logic and arq worker pacing.
PR #75: fix(campaigns): fix paced campaign accumulator and completion status
2026-05-26 04:13:00 UTC
campaigns
Fixed paced campaign execution bugs in the campaign worker. Modified execute_campaign to initialize sent and failed counts from the database instead of resetting them to 0 on every execution run, preventing overwrite of progress. Standardized completion status updates to only transition the campaign to COMPLETED if all recipients are accounted for (sent + failed >= total_recipients). Bound org_id to the worker logger for improved tracing. Resolved a pre-existing TypeError in campaigns router tests where the database session mocked result returned coroutines instead of lists from all() and scalars().all(). Added comprehensive tests covering paced campaign progress accumulation and completion transitions.
PR #81: perf(campaigns): in-place parameter updates for draft campaign updates
2026-05-25 04:30:00 UTC
campaigns
Optimized update_campaign for DRAFT campaigns. Separated template parameter updates from audience changes. Rebuilding (deleting and recreating) recipients is now only triggered when audience fields (recipient_source, contact_ids, tags, csv_data, or smart_rules) change. If only template_params changes, recipients are updated in-place via an offset batch loop (1000 contacts per batch), re-resolving contact template parameters and pre-fetching custom fields without dropping database rows. Added unit test test_update_draft_only_template_params_inplace verifying correct behavior.
PR #80: fix(campaigns): prevent arq job overlap and refine autopilot progress semantics
2026-05-25 04:20:00 UTC
campaigns
Fixed arq job duplicate execution and overlap. Refactored run_smart_campaigns to query arq JobStatus in Redis; skips enqueueing if a job is already queued/active, and cleanly deletes complete/failed job and result keys before re-enqueueing to prevent arq duplicate rejection. Refined progress bar and metrics UI semantics for continuous campaigns: replaced progress percentage/bar with an Autopilot Execution status and active enrollment metrics display, and updated the start confirmation prompt to clarify continuous behavior. Added unit tests for JobStatus active-skipping and inactive key deletion.
PR #None: fix(campaigns): review follow-ups — opt-in, legacy template_params, schedule CTA
2026-05-25 12:00:00 UTC
campaigns
Implemented code-review follow-ups on feature/smart-campaigns: enforce WhatsApp opt-in on manual contact picks and CSV enrollment (optional opted_in column; new CSV contacts default not opted-in unless column is true); legacy template_params list format ignored safely via normalize_template_params_mapping; preflight contact/CSV counts respect opt-in; cap smart_rules conditions at 50 in schema; fix wizard footer to show Create & Schedule only for scheduled send_option; handle legacy array template_params when opening draft edit. Added pytest and Vitest coverage; extended manual test matrix CAM-093–CAM-101 in docs/manual-tests/src/campaigns.md.
PR #79: perf(campaigns): optimize evaluate_smart_campaign_recipients and tag queries
2026-05-25 04:10:00 UTC
campaigns
Optimized segment and tag-based recipient enrollment performance. Refactored evaluate_smart_campaign_recipients to execute in keyset-controlled batches of 1000 contacts, using execution options with a 30-second query timeout and bulk-resolution of contact custom field values. Optimized tag-based campaign preflight count query, creation, and update contact enrollment queries to perform database-level filtering using PostgreSQL GIN index key-exists (?|) overlap, preventing full opted-in contacts table scans. Added robust unit tests verifying batching, timeouts, count query optimization, and pre-fetched custom field template parameter resolution.
PR #78: feat(campaigns): unify campaign schema validators and enforce service-layer checks
2026-05-25 10:00:00 UTC
campaigns
Refactored cross-field validation rules into a shared Pydantic validation helper function `validate_campaign_data`. Integrated it across CampaignCreate, CampaignUpdate, and CampaignPreflightRequest schemas to cleanly support both full creation and partial update inputs. Reinforced update safeguards by adding strict database model consistency validation checks in the service layer's `update_campaign` method. Added extensive unit tests covering contacts, tags, CSV, and segment updates.
PR #77: sec(campaigns): allowlist contact property filter fields in schemas and query builder
2026-05-25 09:55:00 UTC
campaigns
Allowlisted valid fields for contact properties to 'name', 'email', and 'phone' in campaign creation, updates, preflights, and query compilation. Added strict Pydantic model validation rejecting unknown properties in smart_rules and template_params payloads. Added pytest tests covering validation rejections and query compilation ignoring un-allowlisted attributes.
PR #76: feat(campaigns): expose segment targeting operator selection in wizard
2026-05-25 09:47:00 UTC
campaigns
Exposed the rule combination operator (AND/OR) in the segment targeting rules builder (Step 2) of the campaign creation wizard. Dynamically propagates selection to API preflight and submit payloads. Added comprehensive Vitest tests verifying UI state mapping and payload propagation. Added complex backend query builder tests in pytest verifying compiling segment OR conditions containing tags, temperatures, contact properties, and custom fields.
PR #75: fix(campaigns): fix continuous and paced campaigns premature completion
2026-05-25 03:37:00 UTC
campaigns
Fixed continuous (Autopilot) campaigns and one-time paced campaigns from completing prematurely. Continuous campaigns remain RUNNING indefinitely. One-time paced campaigns remain RUNNING when hitting their daily limits as long as pending recipients exist. Added immediate recipient evaluation on campaign start and scheduled launch. Added frontend row-level cancel/stop controls for running/paused campaigns. Added extensive backend and frontend test suites. Added Pydantic schema validation rules on CampaignUpdate and backend service checks on update_campaign to reject inconsistent payloads and prevent partial updates from omitting smart_rules or scheduling continuous campaigns.
PR #74: feat(campaigns): smart autopilot campaigns and paced broadcasts
2026-05-24 11:00:00 UTC
campaigns
Redesigned CampaignsView to unified setups view, integrated Segment Builder supporting custom fields, added backend paced and continuous autopilot campaign loops with daily budgets, added new test suite. Integrated template variable mapping resolved during recipient enrollment and a 4-step wizard (Setup -> Recipients -> Customize & Preview -> Review) with automated CSV column mapping detection. Added full campaign wizard editing for draft status campaigns, persisting recipient source details and metadata, and supported rescheduling/editing pacing of scheduled campaigns.
PR #73: feat(leads): hot/cold temperature marking
2026-05-20 11:38:31 UTC
leads
Test cases updated.
PR #None: feat(campaigns): align campaign wizard Step 2 audience UX with auto follow-up
2026-05-29 17:30:00 UTC
campaigns
Campaign wizard Step 2 now uses pill selectors (Filter Contacts / Target by Tags / CSV) matching auto follow-up patterns. Extracted shared AudienceRulesSummaryCard, AudienceRulesDrawer, TagAudiencePicker, and audienceRules utils; refactored AutoFollowUpWizard to use them. Segment targeting uses summary card + modal drawer instead of inline builder. Removed manual contact picker from UI; legacy contacts drafts show migration banner and block Next. Default audience source is segment. Review step shows human-readable rule chips. Updated unit tests and manual test matrix CAM-014–CAM-105.
PR #78: refactor(campaigns): extract BroadcastsView into composables and shared messaging primitives
2026-05-30 10:00:00 UTC
campaigns
PR1 of campaigns refactor: reduced BroadcastsView.vue from ~2620 lines to ~300 lines by extracting broadcast wizard (4 steps), list, detail, and edit modal into components under frontend/src/components/campaigns/broadcast/. Added shared messaging primitives: WizardStepper, WizardFooter, PreflightBanner, TemplateParamMappingEditor, RecipientSourcePills, EstimatedAudienceBadge; composables useIntervalPoll, usePreflight, useCampaignFormAssets, useCsvRecipients; campaign composables useCampaignList, useCampaignWizard, useCampaignDetail, useCampaignPolling, useCampaignEditModal. Extended waTemplateParams.ts (validation, init, preview) and types/campaign.ts. Renamed step validators to stepRecipientsValid / stepTemplateMappingValid. Migrated broadcasts to waTemplateParameterSlots for template vars. All 18 BroadcastsView.spec.ts + new unit tests pass; npm run build clean.
routing
Broadcasts tab URL remains /campaigns/broadcasts; /campaigns still redirects to broadcasts default child route.
PR #78: refactor(campaigns): broadcast extraction + AFU shared primitives (PR1+PR2)
2026-05-30 12:00:00 UTC
campaigns
PR2: AutoFollowUpWizard now uses shared WizardStepper, WizardFooter, PreflightBanner (nurture console), TemplateParamMappingEditor; followUpSummary.ts for trigger/stop summaries; waTemplateParams validation/preview helpers. NurtureSequenceTab uses triggerChipLabel from followUpSummary and useIntervalPoll for detail refresh.
nurture
Manual QA: /campaigns/sequences — create/edit auto follow-up wizard (5 steps), review preflight console, template param mapping per message step, list trigger chips unchanged.
PR #79: feat(nav): Library module + Automation child routes (IA restructure)
2026-06-01 16:45:00 UTC
library
New top-level Library nav (/library) with child routes: /library/templates (Meta WA templates), /library/forms (WA Flows), /library/quick-replies (canned responses), /library/saved (custom templates). useLibraryAccess composable gates nav and tabs by templates:read, campaigns:read, canned_responses:read, automation:read. navigation.library org flag (default on) in admin module visibility.
automation
Automation refactored to AutomationLayout + child routes: /automation/flows, /keywords, /ai, /kb (no query tabs). WA Flows and canned responses removed from Automation. Backend: manage_automations replaced with automation:write on wa-flows and chatbot-flows mutating endpoints.
campaigns
Campaigns tabs trimmed to Broadcasts + Follow-ups only; WA Templates tab removed (content in Library). Tab labels renamed from Campaigns/Auto follow-ups.
routing
Removed legacy redirects (/templates, /canned-responses, /template-library, campaigns ?tab=). Sidebar grouped Work / Customers / Insights. No backward-compat URL redirects per product decision.
PR #80: feat(ui): unified module page headers (Campaigns pattern)
2026-06-01 22:10:00 UTC
ui
Added shared ModulePageHeader component matching Campaigns chrome (text-xl title, px-4 py-4 sm:px-6 lg:px-8 lg:py-5, bordered tab pills). Migrated Library, Automation, Catalog, Contacts, Analytics, Settings, and Campaigns layouts. Removed header subtitles per product decision. Analytics tabs now use bordered pills; date presets in toolbar slot. Contacts actions in toolbar; mobile Add in after-tabs. Catalog product count in trailing slot. Settings title band only (vertical section nav unchanged). Inbox and Admin out of scope.
PR #80: feat(ui): unified module page headers, split library templates and snippets, and move leads to first tab on contacts page
2026-06-01 23:10:00 UTC
ui
Removed the segmented controller sub-menu in Library -> Templates. Split the view into two top-level tabs directly under Library: 'WhatsApp Templates' (WhatsApp Meta Templates Panel) and 'Custom Snippets' (My Templates Tab). Renamed 'Approved Templates' label to 'WhatsApp Templates' and updated its header layout to match the 'WhatsApp Flows' header style. Updated 'Custom Snippets' (MyTemplatesTab.vue) header layout and button actions to standard subheader styles, and aligned the indigo colors to match the app-wide brand-primary theme. Refactored the Campaigns Auto Follow-ups 'sequences' list view (SequencesView.vue & NurtureSequenceTab.vue), the Analytics dashboard tab container (AnalyticsShell.vue), the Contacts view (ContactsView.vue), and the Leads view (LeadsView.vue) to render the configuration options, search bars, filters, and action buttons inside a fixed full-width subheader utility bar. Reordered the tabs on the Contacts page so that 'Leads' is the first tab and defaults to active. Registered '/library/snippets' child route, updated useLibraryAccess.ts, and added LibrarySnippetsView.vue. Added unit tests for Contacts default tab and tab order. All frontend and backend tests passed and type check builds cleanly.
PR #81: feat(campaigns): in-app help page for broadcasts and follow-ups
2026-06-01 09:55:00 UTC
ui
Added Help & Guide tabs to Campaigns (/campaigns/help), Automation (/automation/help), and Library (/library/help). Apple-inspired scrollable guides with sticky section nav, hero, jump cards, use-case grids, tool/section cards, and quick-start checklists. Added scroll fade indicators (linear-gradient masks) to navigation headers across all help views. Added in-context helper deep-links inside the Broadcast Campaign wizard steps linking to send modes, audience, template variables, and preflight checklist guides. Integrated educational helper guide links into the empty states for Campaigns (CampaignListPanel) and Automation Flows (FlowBuilderTab). Resolved test file compilation issues for AutomationHelpView, LibraryHelpView, and LibraryLayout specs by completing ModuleVisibilityMap fields. Enriched guide content in campaignsHelp.ts, automationHelp.ts, and libraryHelp.ts with WhatsApp messaging limits, opt-out guidelines, template media sizing, and template rejection resolution steps.
PR #82: feat(campaigns): contact audit trail for campaign lifecycle events
2026-06-02 12:00:00 UTC
backend
Contact audit log now records campaign_sent (worker), campaign_delivered/read/failed (webhook status updates), and campaign_failed on send errors. Webhook skips duplicate status updates when recipient status unchanged. Fatal Meta API abort bulk-inserts campaign_failed audits for remaining pending recipients via contact_audit_service helpers (no phone PII in text).
ui
ContactAuditLog shows campaign event labels from data.campaign_name/error. Campaign detail panel shows error_message for failed recipients. Analytics Campaigns tab adds Avg Delivery Rate % and Total Failed stat cards (API fields already existed).
tests
Added webhook campaign status + idempotency tests; worker success audit (no text PII) and fatal-abort bulk audit test.
PR #84: feat(contacts): customer self-service marketing opt-out (STOP/START)
2026-06-03 16:10:00 UTC
backend
New contact_consent_service handles exact-match STOP/UNSUBSCRIBE/START keywords on inbound WhatsApp (chatbot_engine step 0). Opt-out sets is_opted_in=false and opted_out_at, cancels active nurture enrollments (stopped_reason=opt_out), logs opted_out audit. Opt-in reverses state. Manual contact is_opted_in toggle delegates to same service. Nurture enroll_in_sequence rejects opted-out contacts; nurture_worker cancels sends for opted-out. Campaign opt-in filtering unchanged.
ui
Contacts edit modal adds Marketing opt-in toggle (PUT is_opted_in). Chat ContactPanel shows opted-out date. Automation help notes platform STOP handling (no manual keyword rule required). API ContactResponse includes opted_out_at.
tests
pytest: test_contact_consent_service, TestConsentInbound, nurture opted-out enrollment rejection. Vitest: ContactsView is_opted_in on save, ContactPanel opted-out date.
PR #85: fix(templates): catch invalid Meta payloads and surface detailed API errors
2026-06-05 05:00:00 UTC
backend
Strengthened wa_template_validation before Meta submission: reject bare https:// URL buttons, AUTHENTICATION templates without OTP (COPY_CODE/ONE_TAP) buttons, non-numeric flow_id. Added prepare_template_components_for_meta() to strip extra fields. template_service logs component_summary on create. error_adapter prefers Meta error_user_msg. whatsapp_account_service downgrades expected WhatsAppAPIError from exception traceback to warning.
ui
WhatsAppMetaTemplatesPanel client validation rejects Visit-website URLs without hostname (e.g. default https://).
tests
pytest: URL hostname, AUTHENTICATION OTP rules, component sanitization, error_user_msg in error_adapter. Run: pytest tests/services/test_wa_template_validation.py tests/test_error_adapter.py
PR #86: Fix autopilot duplicate sends when frequency cap is enabled
2026-06-05 12:00:00 UTC
backend
Fixed build_smart_audience_query frequency_cap_days exclusion: pending recipients and recent failed rows now block re-enrollment; successful sends still respect sent_at cutoff for intentional re-messaging after cap. Migration 0078_campaign_pending_uq adds partial unique index uq_campaign_recipients_pending_contact. Five campaign invariants hardened: (1) bulk_insert_pending_recipients with ON CONFLICT DO NOTHING + in-batch dedupe for all enrollment paths (contacts/tags/csv/segment/autopilot); (2) frequency_cap_days validated >= 1 in CampaignCreate/Update schemas; (3) reserve_campaign_daily_send_slot atomically reserves one daily send slot per message at dispatch time, with opt-out re-check and pause/cancel checks per recipient; (4) try_enqueue_campaign_execution shared helper with stable _job_id campaign:{id} and defer_until for scheduled campaigns; (5) _get_pending_recipients uses FOR UPDATE SKIP LOCKED. Autopilot cron enqueues when pending backlog exists even if added=0. Webhook wamid lookup scoped by org_id. Fatal abort failed_count uses actual pending row count.
ui
campaignsHelp autopilot section clarifies frequency cap semantics and that pending contacts are never double-enrolled.
tests
178 tests pass: pytest tests/whatsapp/test_smart_campaigns.py tests/services/test_campaign_service.py tests/workers/test_campaign_worker.py tests/routers/test_campaigns.py tests/test_campaign_schemas.py. Covers freq-cap SQL exclusion, double-enroll prevention, bulk_insert dedupe, daily send slot reservation, opt-out at send time, stable job id, SKIP LOCKED, schema validation for frequency_cap_days.
remediation
Pause affected autopilot campaign immediately. Run docs/manual-tests/autopilot-dedupe-remediation.sql to remove duplicate pending rows. Deploy PR #86 + migration 0078, verify zero duplicate pending rows, then resume campaign.
PR #88: feat: move whatsapp template create and edit flows to separate sub-pages and add AI template drafting support
2026-06-08 03:10:00 UTC
backend
Added AI-assisted WhatsApp template drafting. Integrated with credential resolver (supports org custom credentials and fallback platform keys) and limit_service.py for monthly template LLM call limits. Resolved a TOCTOU race condition by pre-incrementing Redis counters. Prevented 500 error leakages by catching LLM provider exceptions. Relaxed multi-variant count parser constraint. Prefilled opt-out footer text for marketing templates.
ui
Migrated WhatsApp template creation and editing from inline forms to separate sub-pages: LibraryTemplatesCreateView and LibraryTemplatesEditView under the library router. The new views support a split 2-column layout with the component fields on the left and dynamic live preview on the right. Highlighted WABA Templates tab state persists correctly inside LibraryLayout. Added opt-out text nudges for MARKETING category template footers and pre-filled the footer text dynamically with "Send 'STOP' to opt-out" if the category is MARKETING.
tests
Added backend unit tests for template AI draft API, monthly LLM call limits, and template draft orchestration. Fixed pre-existing mock validation failures in test_auth.py. Added frontend unit tests in LibraryTemplatesViews.spec.ts testing route loading, form input updates, footer opt-out nudge display, footer text auto-prefilling/clearing on category changes, dynamic live preview rendering, and API submit payloads. Added useLibraryAccess.spec.ts route permission coverage. Verified all 359 vitest tests pass.
PR #90: Redesign Settings page with grouped navigation and progressive disclosure
2026-06-08 09:25:00 UTC
backend
No backend changes. Legacy `/settings?tab=*` query params redirect to new route paths via settingsRoutes beforeEnter.
ui
Replaced monolithic SettingsView (11 flat tabs) with SettingsLayout and 14 route-based sub-pages grouped under You, Workspace, Access & Security, and Integrations. Split Profile into Profile + Sign-in & Security; Organization into General, Conversations, and Compliance. WhatsApp Meta overview moved to collapsible Connection details; account row actions consolidated into overflow menu. Members role edit and Roles permission matrix moved to slide-over panels. TeamsView supports embedded mode without duplicate header. Added settings search, mobile section picker, SettingsGroup/SettingsRow components, and plain-language read-only permission banners.
tests
26 frontend vitest cases: SettingsView.spec.ts (20) covers all major routes, permission redirects, search input, and legacy behavior; settingsNavigation.spec.ts (6) covers legacy tab redirects, route name mapping, group separation, and access helpers. Frontend build passes.
PR #90: Redesign Settings page with grouped navigation and progressive disclosure
2026-06-08 10:35:00 UTC
backend
No backend changes.
ui
Settings UX polish (P0–P2): SettingsRow always stacked for full-width inputs; workspace forms widened to max-w-2xl; profile verification badges moved below email/phone; embedded TeamsView stacks on mobile (lg split pane). Standardized button system: btn-ghost, btn-danger, btn-facebook in main.css; FacebookConnectButton component; SettingsFooterActions for slide-over/modal footers. SettingsPageHeader shows per-page H1 + description from SETTINGS_PAGE_DESCRIPTIONS (duplicate intros removed from child views). Notifications mobile card layout. ContactCustomFields and FormTokensTab aligned to settings design tokens (.input, .label, btn-*). Members pending invites use btn-ghost/btn-danger; WhatsApp connect uses btn-facebook.
tests
20 SettingsView.spec.ts cases pass; frontend production build passes.
PR #None: Unify email and phone verification UX across gate and settings
2026-06-08 11:45:00 UTC
backend
Implemented pending-change phone verification flow. Added 'pending_phone' to VerificationToken model and corresponding database migration 0081. Updated /verify/phone/send and /verify/phone endpoints to store/read pending phone on token, preventing pre-verification user state corruption. Integrated team CRM contact link cleanup/upsert upon successful phone change.
ui
Unified Profile settings page (renamed from Profile & security): read-only rows with Edit/Update/Change modals for full name, email, phone, and password. Phone change uses OTP-first flow via ChangePhoneNumberModal; number is not saved until verification succeeds. Email resend inline under unverified email; #profile-email and #profile-phone hashes open the relevant modal. Merged former Sign-in & Security nav into Profile; legacy /settings/you/security redirects to profile#profile-phone. App banner deep-links to profile phone section. Shared verification components on /verify gate.
tests
verificationRouting.spec.ts, auth-navigation unverified redirect, SettingsView.spec.ts (21), test_auth phone OTP pending_phone cases, test_token_service pending_phone. Frontend build passes.
PR #None: Improve Workspace Settings pages logic, inputs, and layout
2026-06-08 11:39:00 UTC
backend
No backend changes. Verified that backend schemas accept empty keyword arrays by omitting them from front-end payloads or using fallback defaults.
ui
Compliance: updated form to allow saving empty keywords/confirmations when auto opt-out is disabled, using fallback defaults in the API payload. Contact fields: removed vertical grip handles to prevent mock drag-and-drop confusion. WhatsApp: prefetched facebook embed config to eliminate popup blockers, reduced closed-polling interval to 200ms, and added case-insensitive quality color mappings. Conversations: radio groups for reopen behavior and idle timeout (disabled/preset/custom), with remembered day count when toggling reopen modes. SettingsView spec updated.
tests
consentSettings.spec.ts updated with auto-opt-out false assertions, SettingsView.spec.ts updated with new labels. Checked frontend type-checks cleanly and vitest (382) & pytest (1451) suites pass successfully.
PR #93: Add Settings Help & Guide tab
2026-06-08 17:36:00 UTC
backend
No backend changes.
ui
Added fifth Settings header tab Help & Guide at /settings/help. Full-page SettingsHelpView mirrors Library help pattern: hero, use cases, four settings groups overview, permission-filtered page sections with Open links, permissions explainer, quick-start checklist, and external marketing guide links. SettingsLayout hides sidebar, search toolbar, and page header on help route. Content lives in settingsHelp.ts.
tests
settingsHelp.spec.ts (7), SettingsHelpView.spec.ts (4), SettingsView.spec.ts extended with help tab navigation and layout tests (26 total). Frontend build and vitest pass.
PR #94: Show campaign recipients in inbox (WATI-style Campaign status)
2026-06-08 18:05:00 UTC
backend
Campaign worker now calls get_or_create_conversation_for_campaign + message_service.send_template_with_sender for each successful send, persisting Message rows with automation_metadata source_type=campaign. Added ConversationStatus.CAMPAIGN enum + migration 0082. Default list_conversations excludes campaign status; status=campaign filter returns campaign threads. Inbound replies promote campaign -> open via update_conversation_on_message.
ui
Added Campaigns filter tab in Chat inbox (ConversationList + chat store). Campaign threads visible under Campaigns, not default All view.
tests
Updated test_campaign_worker.py with inbox persistence mocks; added conversation_service tests for campaign exclusion/promotion; chat.spec.ts campaigns filter test. pytest campaign+conversation tests (66) and frontend vitest pass.
PR #97: CRM integration hub: contact sync API + Settings connections UI
2026-06-08 17:35:00 UTC
backend
Migration 0083 adds integration_connections (with health columns), external_entity_links, integration_sync_log, integration_idempotency_keys. New app/integrations module: connection CRUD (GET/POST/PATCH /api/integrations/v1/connections), contact upsert (PUT /contacts) and lookup (GET /contacts/by-external/{id}) with machine Bearer auth + X-Integration-Connection-Id. Auto-provisions API key with integration contact scopes on connection create. Idempotency-Key support, phone collision 409, opt-out resubscribe denial. Permissions integrations:read and integrations:manage seeded. Added Redis Lua token-bucket rate limiter at 20 req/sec per connection on /contacts. Added daily arq cleanup worker task (2:30 AM) to prune sync logs (> 30 days) and expired idempotency keys. Refactored tag updates to use set comparison (order-insensitive). Optimized custom fields update comparison to prevent redundant DB writes.
ui
Settings → Integrations → CRM connections page: list connections with health badges (Healthy/Error/Not synced/Inactive), last sync/success/error timestamps, create form (provider/name/webhook), one-time display of connection ID + org ID + API key after create, inline edit and activate/deactivate. Updated Odoo Integration Guide with rate limit warnings.
tests
tests/integrations/ (28 pytest), including rate limiting, worker db pruning, set-based tag comparison, and custom fields optimization checks. integrationConnectionHealth.spec.ts (4), SettingsIntegrationsConnectionsView.spec.ts (2), settingsNavigation.spec.ts updated. Run alembic upgrade head before testing.
PR #98: chore: ruff import and style cleanup
2026-06-09 12:00:00 UTC
backend
No behavior changes. Import reordering (isort/ruff), unused import removal, modern type hints in migration files (Union to pipe syntax, collections.abc.Sequence), datetime.UTC usage. 93 files across app/, tests/, migrations/, mcp_bizbotify/, scripts/.
ui
No frontend changes.
tests
pytest 1485 passed, 26 skipped. No test logic changes; import cleanup only.
PR #100: Integration hub Phase 2: message send + media upload API
2026-06-10 12:00:00 UTC
backend
Migration 0084 adds integration_media_uploads (sha256 dedupe, promote retry fields, RLS) and messages.integration_awaiting_upload_id + integration_send_enqueued. New endpoints: POST/GET /api/integrations/v1/messages (async 202), POST /media/upload (202), GET /media/uploads/{id}. message_send_service validates recipients, 24h window, template approval, upload readiness; enqueues send_integration_message via arq. integration_worker promotes uploads to Meta (3 attempts, backoff), fan-outs pending media messages, cleanup cron. Default connection API key scopes now include integrations:messages:read/write. Requires connection config default_whatsapp_account_id.
ui
No frontend changes.
tests
56 pytest across test_message_send_service (11), test_integrations_router_messages (14), test_template_send_validation (8), test_media_upload_service (8), test_integration_worker (15), is_messaging_window_open (3). Covers cleanup cron, promote fan-out enqueue, template validation, media upload HTTP, idempotency, 429, scope errors. Run alembic upgrade head and ensure arq worker running before manual QA.
PR #102: Integration API: GET templates catalog for Odoo connectors
2026-06-11 12:00:00 UTC
backend
New Meta-shaped GET /api/integrations/v1/templates (defaults: status=APPROVED, category=UTILITY, language=en), GET /templates/{meta_template_id}, GET /templates/by-name/{name}?language=. Each data[] item mirrors Meta message_templates (id, name, language, status, category, components) plus _bizbotify extension (template_id, whatsapp_account_id, parameter_slots). GET /media/uploads/{id} now returns meta_media_id when ready. Scope: integrations:messages:read.
ui
No frontend changes.
tests
24 pytest: template list/get router + service tests including invalid_status (400), language=all/category=all pagination, blank meta_template_id SQL filter, meta_media_id on ready upload GET. Manual: sync templates, GET /templates with defaults, invalid status=FOO returns 400, GET ready upload shows meta_media_id.
docs
odoo-partner-quickstart.md §6 expanded to full templates API reference (list/get/by-name, parameter_slots, catalog vs send shapes, Odoo sync pattern, errors, Python client helpers). Messages/Media renumbered to §7–§8.
PR #104: Campaign live updates via WebSocket
2026-06-12 09:35:00 UTC
backend
Added WSEventType.CAMPAIGN_UPDATE and broadcast_campaign_update() in campaign_service (CAMPAIGN_PROGRESS_BROADCAST_EVERY=100, should_broadcast_progress helper). Worker persists sent_count/failed_count each batch commit, broadcasts on 100-send milestones and on terminal status (completed/cancelled/fatal abort/monthly limit). pause_campaign, cancel_campaign, and webhook-driven completion also broadcast. Skips WS when Redis unavailable.
ui
New useCampaignLiveUpdates composable on BroadcastsView subscribes to campaign_update WS events and patches list + detail without tab refresh. useCampaignPolling now uses immediate watch and fires one refresh when polling starts (WS outage fallback).
tests
Backend: TestShouldBroadcastProgress, TestBroadcastCampaignUpdate, TestCampaignBroadcastOnStateChange, worker completion broadcast assertion. Frontend: useCampaignLiveUpdates.spec.ts (3), extended useCampaignPolling.spec.ts (4). Manual: start campaign with detail open — status flips to Completed without Refresh; list-only view updates badge; two tabs sync on pause; large campaign sent_count ticks at 100/200.
docs
No doc changes.
PR #106: Analytics Overview daily dashboard redesign
2026-06-12 11:10:00 UTC
backend
No new endpoints; Overview fetches response-times + SLA/conversation trend series for sparklines.
ui
Overview redesigned as daily dashboard: WhatsApp health score strip (composite SLA/automation/resolution/leads minus alert penalty), three sparkline cards (messages, SLA, conversation activity) with deep links to Conversations tab, Key metrics section (former hero KPIs), deep links on status donut and automation wins. New Customize widgets: Health score, Daily trend sparklines.
tests
analyticsHealthScore.spec.ts, OverviewHealthStrip.spec.ts; updated OverviewTab specs for new widget keys.
docs
No marketing doc changes in this commit.
PR #106: Analytics UX Phase 1: trust, coherence, and actionability
2026-06-12 12:00:00 UTC
backend
Unified date filtering: GET /analytics/messages accepts start_date/end_date; pipeline-health and work-queue endpoints accept date range (latest stat_date in range). New GET /analytics/freshness returns org_dashboard and agent_leads pipeline updated_at for footer display.
ui
Default date preset 30d. Message volume chart uses global date bar (period rollup only). Alerts are tappable and deep-link via link_tab. Consistent error+Retry banners on all tabs. Leads tab respects Customize widget toggles. Metric tooltips on Overview/Conversations KPIs (SLA 60 min disclosed). CSV export on agent/team/campaign/template/flow/leads tables. Footer shows Data as of from pipeline metadata. Tab rename Bot Performance→Automation with mobile labelShort. Customize button (was Configure Widgets). Custom date validation.
tests
Backend: test_analytics_freshness_endpoint. Frontend: OverviewTab.alerts.spec (alert navigation), AgentCrmTab date params + widgetVisibility, analyticsExport.spec. Manual: verify 30d default; change dates and confirm message volume + Leads pipeline/queue update; click alert pill navigates tab; hide Leads widgets via Customize; export CSV from Conversations agent table; confirm footer freshness timestamp; marketing guide matches tabbed UI.
docs
guide-analytics.html updated for tabbed IA, KPI names, 30d default, Customize/Export CSV.
PR #107: Org default WhatsApp account and settings ID disclosure
2026-06-12 15:10:00 UTC
backend
Migration 0085 adds organizations.default_whatsapp_account_id (FK, backfill single active account). PUT /whatsapp-accounts/default with settings:write, audit log org.default_whatsapp_account_changed, auto-default on first account connect, clear on disable/delete. Integration resolve_whatsapp_account_id falls back: request → connection config → org default.
ui
WhatsApp settings: helper text for org default; Default badge; Set as default menu; collapsible Account identifiers grouped Meta (phone number ID, WABA ID) vs BizBotify API (account UUID) with copy. Required dropdowns pre-select org default via resolveInitialWhatsappAccountId. Flow/keyword/AI 'All accounts' dropdowns unchanged.
tests
pytest tests/services/test_whatsapp_org_default.py (8). Frontend: whatsappAccountDefault.spec, SettingsWorkspaceWhatsappView.spec, SettingsView whatsapp smoke, useCampaignWizard pinia fix. Manual: set default on multi-account org; expand identifiers and copy; campaign wizard pre-fill; Odoo integration without connection default_whatsapp_account_id when org default set; disable default account clears org default.
docs
odoo-partner-quickstart.md precedence note; settingsHelp WhatsApp steps updated.
PR #108: fix: scope inline contact tag input to each table row
2026-06-12 16:05:00 UTC
backend
No backend changes.
ui
Contacts table inline tag input now uses per-contact draft state (newTagsByContactId) instead of a single shared ref. Typing in one row's '+ tag' field no longer mirrors text across all contacts; Enter still saves only to that contact.
tests
Frontend: ContactsView.spec.ts — new test 'keeps inline tag draft scoped to the contact row being edited' (23 tests pass). Manual: load Contacts with 2+ contacts; type a new tag in one row only; confirm other rows' tag inputs stay empty; press Enter and verify tag appears only on that contact.
docs
None.
PR #109: fix: always show all tags in contact filter dropdown
2026-06-12 16:40:00 UTC
backend
GET /api/contacts/tags returns distinct sorted tag names for the org via jsonb_array_elements_text (tenant-scoped, contacts:read). contact_service.list_distinct_tags with org_id logging on failure.
ui
Contacts tag filter dropdown uses useContactTags composable — options no longer shrink when a tag is selected. Campaign broadcast wizard, AudienceRulesDrawer datalist, and AutoFollowUpWizard tag picker use the same /contacts/tags source instead of deriving tags from the first 100 contacts.
tests
Backend: test_contacts_router_tags.py (route ordering, tenant scope), test_contact_list_distinct_tags.py. Frontend: useContactTags.spec.ts, ContactsView.spec.ts regression 'keeps all tag filter options after selecting a tag'. Manual: Contacts → pick tag filter → confirm all tags still in dropdown; verify campaign/nurture tag pickers list tags beyond first contact page.
docs
None.