# QA Unit Audit Report

Date: July 2, 2026
Product: VejalNai Chatbot
Audit type: Unit-by-unit product and engineering QA
Status: Strong beta foundation, not production-ready

## Executive Summary

The application has a working Laravel foundation for a support inbox, public chat widget, bot matching, unknown-question handoff, callback requests, teams, appearance settings, and inbound Meta webhooks. The main product risk is that some workflows look complete in the UI but do not complete the real operational loop yet.

The most important release blockers are outbound channel delivery, consistent privilege enforcement, webhook idempotency, localized text integrity, and production session/security hardening for the widget.

## Verification Baseline

| Check | Result | Notes |
| --- | --- | --- |
| Route list | Passed | 39 routes registered. |
| HTTP `/health` | Passed | Returned 200 with `vejalnai-chatbot` status `ok`. |
| HTTP `/ready` | Passed | Returned 200 with database `ok`. |
| HTTP `/api/health` | Passed | Returned 200 with `vejalnai-api` status `ok`. |
| HTTP `/widget.js` | Passed | Returned 200, JavaScript content type, 30,838 bytes. |
| PHPUnit | Passed | 44 tests, 186 assertions. |
| PHP lint | Passed | 113 PHP files checked. |
| `php artisan config:cache` | Passed | Configuration cache builds. |
| `php artisan route:cache` | Passed | Route cache builds. |
| `php artisan view:cache` | Passed | Blade view cache builds. |
| Pint | Failed | 7 files need style fixes. |
| Migrations | Passed | All migrations ran in current local database. |

## Unit Audit Matrix

| Unit | Files | Current behavior | QA status |
| --- | --- | --- | --- |
| Runtime bootstrap | `public/index.php`, `bootstrap/app.php`, `bootstrap/providers.php` | Standard Laravel 13 entry flow. | Pass. |
| Web routes | `routes/web.php` | Serves landing, health, ready, widget script, auth pages, dashboard pages, settings, teams, conversations. | Pass with authorization gaps. |
| API routes | `routes/api.php` | Serves widget config/messages and Meta webhook verify/receive endpoints. | Pass with security/idempotency gaps. |
| Authentication | `AuthenticatedSessionController`, `LoginRequest` | Session login/logout works and tests pass. | Pass. |
| Organization resolution | `ResolvesCurrentOrganization` | Picks the first user membership. | P2 risk for future multi-organization switching. |
| Membership privileges | `Membership`, `TeamMemberController` | Roles and JSON privileges exist. Team create/update/delete enforces `manage_team`. | Partial pass. Enforcement not applied across all write routes. |
| Dashboard | `DashboardController`, `resources/views/dashboard.blade.php` | Shows counts and operational navigation. | Pass for prototype. |
| Conversations list | `ConversationController`, inbox Blade views | Lists conversations, details, messages, current page, assignment controls. | Pass with scale risk due full query list and polling. |
| Conversation polling | `ConversationMessageController@index` | Authenticated members can poll messages and current page. | Pass with privilege gap: membership only. |
| Human replies | `ConversationMessageController@store` | Stores local human messages and supports JSON response for AJAX UI. | P0: does not send replies to Facebook/WhatsApp. |
| Assignment | `ConversationAssignmentController` | Requires `assign_conversations` or `take_over_handoffs`. | Pass. |
| Bot matching | `MatchIncomingMessage` | Normalizes messages, applies typo corrections, scores exact, contains, token, and fuzzy matches. | Pass for MVP. Needs evaluator coverage before scale. |
| Incoming message processor | `ProcessIncomingCustomerMessage` | Creates customer messages, bot replies, clarifications, unknown questions, callbacks, and human takeover. | Partial pass. Contains mojibake localized command text. |
| Unknown questions | `UnknownQuestionController`, `UnknownQuestionResolutionController` | Lists unknowns, resolves into existing/new intents and templates. | P1: no `train_ai` privilege enforcement. |
| Callback requests | `CallbackRequestController`, `CallbackRequestCompletionController` | Lists and completes callback tasks. | P1: complete action authorizes membership, not `manage_callbacks`. |
| Public widget script | `WidgetScriptController` | Serves self-contained JavaScript chat widget with appearance config, quick replies, history polling, reply context, and clear history. | Pass for beta. P1 session/history model is weak for production. |
| Widget config API | `WidgetConfigController` | Returns saved widget appearance and quick replies with CORS. | Pass with open-origin risk when origins are empty. |
| Widget message API | `WidgetMessageController` | Creates/updates contacts, conversations, messages, bot results, and history. | Pass for web beta. Needs server-issued session token and rate limiting. |
| Appearance settings | `AppearanceSettingsController`, `settings/appearance.blade.php` | Saves title, subtitle, colors, gradient/solid header, logo, quick replies, position, clear/close controls, origins. | P1: no `customize_widget` enforcement. |
| Channel settings | `ChannelIntegrationController`, `settings/channels.blade.php` | Saves Facebook/WhatsApp connection data, encrypted access token and app secret. | P1: no `manage_channels` enforcement. Connection is inbound/config only. |
| Meta webhooks | `MetaWebhookController` | Verifies webhook challenge, receives Facebook and WhatsApp messages, creates contacts/conversations, runs bot processor. | P0: no idempotency. P1: signature verification can stay off. |
| Channel secrets | `ChannelIntegration` | `access_token` and `app_secret` use encrypted casts. | Pass. |
| SDKs | `sdk/README.md`, Android, iOS clients | Starter clients post messages to `/api/widget/messages`. | P2: no history, typed errors, push, delivery status, or package distribution. |
| Seeder | `DatabaseSeeder` | Creates admin, org, widget key, delivery-charge intent, demo conversation. | Pass for local. P1 if default credentials are used outside local. |
| Runtime config | `.env.example`, `docker-compose.yml` | Example uses PostgreSQL/Redis; current local runtime reports SQLite/file/sync drivers. | P2 documentation/runtime mismatch. |

## Defect Register

### P0-1: Human replies are local-only for external channels

Evidence:

- `ConversationMessageController@store` creates a `human` message.
- No service sends messages to Messenger Send API or WhatsApp Cloud API.

Impact:

- Agents can believe they replied to Facebook or WhatsApp customers when no customer received the message.
- This blocks production launch for omnichannel support.

Required fix:

- Add a channel delivery service and queued jobs.
- Add message delivery fields: `delivery_status`, `provider_message_id`, `delivery_error`, `delivered_at`.
- Add failure UI and retry action.

### P0-2: Webhooks are not idempotent

Evidence:

- Meta webhook events are processed into messages every time a payload is received.
- Provider message IDs are not stored with a unique constraint.

Impact:

- Provider retries or duplicate events can create duplicate customer messages, bot replies, callbacks, and unknown questions.

Required fix:

- Store provider, provider event id, provider message id, and provider timestamp.
- Enforce uniqueness per organization/provider/message id.
- Add duplicate webhook tests for Facebook and WhatsApp.

### P0-3: RBAC is not consistently enforced

Evidence:

- Teams and assignment enforce privileges.
- Appearance update does not require `customize_widget`.
- Channel update/delete does not require `manage_channels`.
- Human reply request requires organization membership, not `reply_to_customers`.
- Unknown-question resolution does not require `train_ai`.
- Callback completion does not require `manage_callbacks`.

Impact:

- Users with low privileges can perform admin or support actions if they are members of the organization.

Required fix:

- Introduce policies or FormRequest authorization per resource/action.
- Add negative authorization tests for every protected write route.

### P1-1: Bangla command text is mojibake in source

Evidence:

- `ProcessIncomingCustomerMessage` contains corrupted Bangla text around the Bangla name command and confirmation response.

Impact:

- Bangla command support can fail or display broken text.
- The target product depends on Bangla/Banglish quality, so this is a high product trust risk.

Required fix:

- Replace corrupted text with valid UTF-8.
- Move localized bot strings into translation files.
- Add tests for Bangla `/name` equivalent and Bangla confirmation response.

### P1-2: Widget history uses weak client-side identity

Evidence:

- The public widget uses `sessionStorage` visitor id.
- History is fetched by `widget_key` plus `visitor_id`.

Impact:

- A public visitor id is easy to change or guess.
- Conversation history access is not strong enough for production support data.

Required fix:

- Issue signed visitor session tokens.
- Bind widget sessions to organization, origin, browser/device metadata, and expiry.
- Rate limit public widget endpoints.

### P1-3: Meta signature verification can be disabled

Evidence:

- Channel settings allow `off`, `sha1`, or `sha256`.
- `MetaWebhookController::signatureAllowed` accepts requests when mode is off or no app secret exists.

Impact:

- Production webhooks can accept spoofed traffic if left disabled.

Required fix:

- Allow `off` only in local/dev.
- Require SHA-256 for production readiness.
- Show channel health state and setup warnings in the UI.

### P1-4: Channel connection is not truly complete

Evidence:

- Facebook/WhatsApp settings and inbound webhook intake exist.
- No outbound delivery, token validation, subscription verification UI, or provider health check exists.

Impact:

- Admins can configure a channel that still does not support the full customer support loop.

Required fix:

- Add connect/test flow, token validation, last inbound/outbound timestamps, and delivery status.

### P1-5: Code style gate fails

Evidence:

- `vendor\bin\pint --test` fails in 7 files.

Impact:

- CI should block production releases until formatting passes.

Required fix:

- Run Pint or manually apply style fixes, then enforce in CI.

## Regression Areas To Add

- External channel outbound reply tests.
- Duplicate webhook retry tests.
- Negative privilege tests for each write route.
- Bangla command encoding tests.
- Widget history authorization tests.
- Rate limiting tests for public widget endpoints.
- Widget appearance contrast and mobile layout tests.
- SDK request/response contract tests.
- Channel setup validation tests.

## QA Recommendation

Do not call this production-grade yet. Treat the current system as a strong beta. The next phase should stabilize core correctness before adding new surface area.
