-
Notifications
You must be signed in to change notification settings - Fork 65
docs(ai-docs): task refactor migration overview (PR 1/4) #644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from 12 commits
439effd
4a534db
45a1bd6
28397c2
3bd613f
606dcbf
23c1fab
e4ea922
b35a8c3
2313f1a
0024a37
abbbaf3
eabc80d
6e50dc5
0d08a09
d576676
4a28a43
91dd405
319b843
86ada36
4471af5
e6ce0da
3c5f274
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| # Migration Doc 001: Task Refactor Migration Overview | ||
|
|
||
| ## Purpose | ||
|
|
||
| This document set guides the migration of CC Widgets from the **old ad-hoc task state management** to the **new state-machine-driven architecture** in CC SDK (`task-refactor` branch). | ||
|
|
||
| --- | ||
|
|
||
| ## Migration Document Index | ||
|
|
||
| | # | Document | Scope | Risk | Priority | | ||
| |---|----------|-------|------|----------| | ||
| | 002 | [002-ui-controls-migration.md](./002-ui-controls-migration.md) | Replace `getControlsVisibility()` with `task.uiControls` | **High** (core UX) | P0 | | ||
| | 003 | [003-store-event-wiring-migration.md](./003-store-event-wiring-migration.md) | Refactor store event handlers to leverage state machine events | **Medium** | P1 | | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| | 004 | [004-call-control-hook-migration.md](./004-call-control-hook-migration.md) | Refactor `useCallControl` hook, timer utils, fix bugs | **High** (largest widget) | P0 | | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| | 005 | [005-incoming-task-migration.md](./005-incoming-task-migration.md) | Refactor `useIncomingTask` for state-machine offer/assign flow | **Low** | P2 | | ||
| | 006 | [006-task-list-migration.md](./006-task-list-migration.md) | Refactor `useTaskList` for per-task `uiControls` | **Low** | P2 | | ||
| | 007 | [007-outdial-call-migration.md](./007-outdial-call-migration.md) | No changes needed (CC-level, not task-level) | **Low** | P3 | | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| | 008 | [008-store-task-utils-migration.md](./008-store-task-utils-migration.md) | Retire or thin out `task-utils.ts`, fix `findHoldTimestamp` dual signatures | **Medium** | P1 | | ||
| | 009 | [009-types-and-constants-migration.md](./009-types-and-constants-migration.md) | Align types/constants with SDK, document `UIControlConfig` | **Medium** | P1 | | ||
| | 010 | [010-component-layer-migration.md](./010-component-layer-migration.md) | Update `cc-components` to accept new control shape from SDK | **Medium** | P1 | | ||
| | 011 | [011-execution-plan.md](./011-execution-plan.md) | Step-by-step spec-first execution plan with 10 milestones | — | — | | ||
| | 012 | [012-task-lifecycle-flows-old-vs-new.md](./012-task-lifecycle-flows-old-vs-new.md) | End-to-end task flows (14 scenarios) with old vs new tracing | — | Reference | | ||
| | 013 | [013-file-inventory-old-control-references.md](./013-file-inventory-old-control-references.md) | Complete file-by-file inventory of every old control reference | — | Reference | | ||
| | 014 | [014-task-code-scan-report.md](./014-task-code-scan-report.md) | Deep code scan findings across both CC SDK and CC Widgets repos | — | Reference | | ||
|
|
||
| --- | ||
|
|
||
| ## Key Architectural Shift | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Before (Old Approach) | ||
| ``` | ||
| SDK emits 30+ events → Store handlers manually update observables → | ||
| Widgets compute UI controls via getControlsVisibility() → | ||
| Components receive {isVisible, isEnabled} per control | ||
| ``` | ||
|
|
||
| **Problems:** | ||
| - Control visibility logic duplicated between SDK and widgets | ||
| - Ad-hoc state derivation from raw task data (consult status, hold status, conference flags) | ||
| - Fragile: every new state requires changes in widgets, store utils, AND component logic | ||
| - No single source of truth for "what state is this task in?" | ||
|
|
||
| ### After (New Approach) | ||
| ``` | ||
| SDK state machine handles all transitions → | ||
| SDK computes task.uiControls automatically → | ||
| SDK emits task:ui-controls-updated → | ||
| Widgets consume task.uiControls directly → | ||
| Components receive {isVisible, isEnabled} per control | ||
|
||
| ``` | ||
|
|
||
| **Benefits:** | ||
| - Single source of truth: `task.uiControls` from SDK | ||
| - Widget code dramatically simplified (remove ~600 lines of control visibility logic) | ||
| - Store utils thinned (most consult/conference/hold status checks no longer needed) | ||
| - New states automatically handled by SDK, zero widget changes needed | ||
| - Parity with Agent Desktop guaranteed by SDK | ||
|
|
||
| --- | ||
|
|
||
| ## Repo Paths Reference | ||
|
|
||
| ### CC Widgets (this repo) | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| | Area | Path | | ||
| |------|------| | ||
| | Task widgets | `packages/contact-center/task/src/` | | ||
| | Task hooks | `packages/contact-center/task/src/helper.ts` | | ||
| | Task UI utils (OLD) | `packages/contact-center/task/src/Utils/task-util.ts` | | ||
| | Task constants | `packages/contact-center/task/src/Utils/constants.ts` | | ||
| | Task timer utils | `packages/contact-center/task/src/Utils/timer-utils.ts` | | ||
| | Hold timer hook | `packages/contact-center/task/src/Utils/useHoldTimer.ts` | | ||
| | Task types | `packages/contact-center/task/src/task.types.ts` | | ||
| | Store | `packages/contact-center/store/src/store.ts` | | ||
| | Store event wrapper | `packages/contact-center/store/src/storeEventsWrapper.ts` | | ||
| | Store task utils (OLD) | `packages/contact-center/store/src/task-utils.ts` | | ||
| | Store constants | `packages/contact-center/store/src/constants.ts` | | ||
| | CC Components task | `packages/contact-center/cc-components/src/components/task/` | | ||
| | CC Components types | `packages/contact-center/cc-components/src/components/task/task.types.ts` | | ||
|
|
||
| ### CC SDK (task-refactor branch) | ||
| | Area | Path | | ||
| |------|------| | ||
| | State machine | `packages/@webex/contact-center/src/services/task/state-machine/` | | ||
| | UI controls computer | `.../state-machine/uiControlsComputer.ts` | | ||
| | State machine config | `.../state-machine/TaskStateMachine.ts` | | ||
| | Guards | `.../state-machine/guards.ts` | | ||
| | Actions | `.../state-machine/actions.ts` | | ||
| | Constants (TaskState, TaskEvent) | `.../state-machine/constants.ts` | | ||
| | Types | `.../state-machine/types.ts` | | ||
| | Task service | `.../task/Task.ts` | | ||
| | Task manager | `.../task/TaskManager.ts` | | ||
| | Task types | `.../task/types.ts` | | ||
| | Sample app | `docs/samples/contact-center/app.js` | | ||
|
|
||
| --- | ||
|
|
||
| ## SDK Version Requirements | ||
|
|
||
| The CC Widgets migration depends on the CC SDK `task-refactor` branch being merged and released. Key new APIs: | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| | API | Type | Description | | ||
| |-----|------|-------------| | ||
| | `task.uiControls` | Property (getter) | Pre-computed `TaskUIControls` object | | ||
| | `task:ui-controls-updated` | Event | Emitted when any control's visibility/enabled state changes | | ||
| | `TaskUIControls` | Type | `{ [controlName]: { isVisible: boolean, isEnabled: boolean } }` | | ||
| | `TaskState` | Enum | Explicit task states (IDLE, OFFERED, CONNECTED, HELD, etc.) | | ||
|
|
||
| --- | ||
|
|
||
| ## Pre-existing Bugs Found During Analysis | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| These bugs exist in the current codebase and should be fixed during migration: | ||
|
|
||
| ### 1. Recording Callback Cleanup Mismatch | ||
| **File:** `task/src/helper.ts` (useCallControl), lines 634-653 | ||
|
|
||
| Setup uses `TASK_EVENTS.TASK_RECORDING_PAUSED` / `TASK_EVENTS.TASK_RECORDING_RESUMED`, but cleanup uses `TASK_EVENTS.CONTACT_RECORDING_PAUSED` / `TASK_EVENTS.CONTACT_RECORDING_RESUMED`. Callbacks are never properly removed. | ||
|
|
||
| ### 2. `findHoldTimestamp` Dual Signatures | ||
| Two `findHoldTimestamp` functions with different signatures: | ||
| - `store/src/task-utils.ts`: `findHoldTimestamp(task: ITask, mType: string)` | ||
| - `task/src/Utils/task-util.ts`: `findHoldTimestamp(interaction: Interaction, mType: string)` | ||
|
|
||
| Should be consolidated to one function during migration. | ||
|
|
||
| --- | ||
|
|
||
| ## Critical Migration Notes | ||
Kesari3008 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### UIControlConfig Is Built by SDK (Not by Widgets) | ||
|
|
||
| Widgets do NOT need to provide `UIControlConfig`. The SDK builds it internally from agent profile, `callProcessingDetails`, media type, and voice variant. This means `deviceType`, `featureFlags`, and `conferenceEnabled` **can be removed** from `useCallControlProps` — they are only used for `getControlsVisibility()` which is being eliminated. **Note:** `agentId` must be retained because it is also used by timer utilities (`calculateStateTimerData`, `calculateConsultTimerData`) to look up the agent's participant record from `interaction.participants`. | ||
|
|
||
| ### Timer Utils Dependency on `controlVisibility` | ||
|
|
||
| `calculateStateTimerData()` and `calculateConsultTimerData()` in `timer-utils.ts` accept `controlVisibility` as a parameter with old control names. These functions must be migrated to accept `TaskUIControls` (new control names). | ||
|
|
||
| ### `task:wrapup` Race Condition | ||
|
|
||
| The SDK sample app uses `setTimeout(..., 0)` before updating UI after `task:wrapup`. Consider adding a similar guard in the hook to avoid control flickering during wrapup transition. | ||
|
|
||
| ### Sample App Reference Pattern | ||
|
|
||
| The CC SDK sample app (`docs/samples/contact-center/app.js`) demonstrates the canonical pattern: | ||
|
|
||
| ```javascript | ||
| task.on('task:ui-controls-updated', () => { | ||
| updateCallControlUI(task); | ||
| }); | ||
|
|
||
| function updateCallControlUI(task) { | ||
| const uiControls = task.uiControls || {}; | ||
| applyAllControlsFromUIControls(uiControls); | ||
| } | ||
|
|
||
| function applyControlState(element, control) { | ||
| element.style.display = control?.isVisible ? 'inline-block' : 'none'; | ||
| element.disabled = !control?.isEnabled; | ||
| } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| _Created: 2026-03-09_ | ||
| _Updated: 2026-03-09 (added deep scan findings, before/after examples, bug reports)_ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| # Migration Doc 002: UI Controls — `getControlsVisibility()` → `task.uiControls` | ||
|
|
||
| ## Summary | ||
|
|
||
| The largest single change in this migration. CC Widgets currently computes all call control button visibility/enabled states in `task-util.ts::getControlsVisibility()` (~650 lines). The new SDK provides `task.uiControls` as a pre-computed `TaskUIControls` object driven by the state machine, making the widget-side computation redundant. | ||
|
|
||
| --- | ||
|
|
||
| ## Old Approach | ||
|
|
||
| ### Entry Point | ||
| **File:** `packages/contact-center/task/src/Utils/task-util.ts` | ||
| **Function:** `getControlsVisibility(deviceType, featureFlags, task, agentId, conferenceEnabled, logger)` | ||
|
|
||
| ### How It Works (Old) | ||
| 1. Widget calls `getControlsVisibility()` on every render/task update | ||
| 2. Function inspects raw `task.data.interaction` to derive: | ||
| - Media type (telephony, chat, email) | ||
| - Device type (browser, agentDN, extension) | ||
| - Consult status (via `getConsultStatus()` from store utils) | ||
| - Hold status (via `findHoldStatus()` from store utils) | ||
| - Conference status (via `task.data.isConferenceInProgress`) | ||
| - Participant counts (via `getConferenceParticipantsCount()`) | ||
| 3. Each control has a dedicated function that returns `{ isVisible, isEnabled }` | ||
| 4. Result includes both control visibility AND state flags (e.g., `isHeld`, `consultCallHeld`) | ||
|
|
||
| ### Old Control Names (22 controls + 7 state flags) | ||
| | Old Control Name | Type | | ||
| |------------------|------| | ||
| | `accept` | `Visibility` | | ||
| | `decline` | `Visibility` | | ||
| | `end` | `Visibility` | | ||
| | `muteUnmute` | `Visibility` | | ||
| | `holdResume` | `Visibility` | | ||
| | `pauseResumeRecording` | `Visibility` | | ||
| | `recordingIndicator` | `Visibility` | | ||
| | `transfer` | `Visibility` | | ||
| | `conference` | `Visibility` | | ||
| | `exitConference` | `Visibility` | | ||
| | `mergeConference` | `Visibility` | | ||
| | `consult` | `Visibility` | | ||
| | `endConsult` | `Visibility` | | ||
| | `consultTransfer` | `Visibility` | | ||
| | `consultTransferConsult` | `Visibility` | | ||
| | `mergeConferenceConsult` | `Visibility` | | ||
| | `muteUnmuteConsult` | `Visibility` | | ||
| | `switchToMainCall` | `Visibility` | | ||
| | `switchToConsult` | `Visibility` | | ||
| | `wrapup` | `Visibility` | | ||
| | **State flags** | | | ||
| | `isConferenceInProgress` | `boolean` | | ||
| | `isConsultInitiated` | `boolean` | | ||
| | `isConsultInitiatedAndAccepted` | `boolean` | | ||
| | `isConsultReceived` | `boolean` | | ||
| | `isConsultInitiatedOrAccepted` | `boolean` | | ||
| | `isHeld` | `boolean` | | ||
| | `consultCallHeld` | `boolean` | | ||
|
|
||
| --- | ||
|
|
||
| ## New Approach | ||
|
|
||
| ### Entry Point | ||
| **SDK Property:** `task.uiControls` (getter on `ITask`) | ||
| **SDK Event:** `task:ui-controls-updated` (emitted when controls change) | ||
| **SDK File:** `packages/@webex/contact-center/src/services/task/state-machine/uiControlsComputer.ts` | ||
|
|
||
| ### How It Works (New) | ||
| 1. SDK state machine transitions on every event (hold, consult, conference, etc.) | ||
| 2. After each transition, `computeUIControls(currentState, context)` is called | ||
| 3. If controls changed (`haveUIControlsChanged()`), emits `task:ui-controls-updated` | ||
| 4. Widget reads `task.uiControls` — no computation needed on widget side | ||
|
|
||
| ### New Control Names (17 controls, no state flags) | ||
| | New Control Name | Type | | ||
| |------------------|------| | ||
| | `accept` | `{ isVisible, isEnabled }` | | ||
| | `decline` | `{ isVisible, isEnabled }` | | ||
| | `hold` | `{ isVisible, isEnabled }` | | ||
| | `mute` | `{ isVisible, isEnabled }` | | ||
| | `end` | `{ isVisible, isEnabled }` | | ||
| | `transfer` | `{ isVisible, isEnabled }` | | ||
| | `consult` | `{ isVisible, isEnabled }` | | ||
| | `consultTransfer` | `{ isVisible, isEnabled }` | | ||
| | `endConsult` | `{ isVisible, isEnabled }` | | ||
| | `recording` | `{ isVisible, isEnabled }` | | ||
| | `conference` | `{ isVisible, isEnabled }` | | ||
| | `wrapup` | `{ isVisible, isEnabled }` | | ||
| | `exitConference` | `{ isVisible, isEnabled }` | | ||
| | `transferConference` | `{ isVisible, isEnabled }` | | ||
| | `mergeToConference` | `{ isVisible, isEnabled }` | | ||
| | `switchToMainCall` | `{ isVisible, isEnabled }` | | ||
| | `switchToConsult` | `{ isVisible, isEnabled }` | | ||
|
|
||
| --- | ||
|
|
||
| ## Old → New Control Name Mapping | ||
|
|
||
| | Old Widget Control | New SDK Control | Notes | | ||
| |--------------------|-----------------|-------| | ||
| | `accept` | `accept` | Same | | ||
| | `decline` | `decline` | Same | | ||
| | `end` | `end` | Same | | ||
| | `muteUnmute` | `mute` | **Renamed** | | ||
| | `holdResume` | `hold` | **Renamed** (hold state still togglable) | | ||
| | `pauseResumeRecording` | `recording` | **Renamed** — toggle button (pause/resume) | | ||
| | `recordingIndicator` | `recording` | **Maps to same SDK control** — but widget must keep a separate UI indicator (status badge). Use `recording.isVisible` for the indicator badge and `recording.isEnabled` for the toggle button's interactive state. See note below. | | ||
| | `transfer` | `transfer` | Same | | ||
| | `conference` | `conference` | Same | | ||
| | `exitConference` | `exitConference` | Same | | ||
| | `mergeConference` | `mergeToConference` | **Renamed** | | ||
| | `consult` | `consult` | Same | | ||
| | `endConsult` | `endConsult` | Same | | ||
| | `consultTransfer` | `consultTransfer` | Same (always hidden in new SDK) | | ||
| | `consultTransferConsult` | `transfer` / `transferConference` | **Split** — `transfer` for consult transfer, `transferConference` for conference transfer | | ||
| | `mergeConferenceConsult` | `mergeToConference` | **Merged** into `mergeToConference` | | ||
| | `muteUnmuteConsult` | `mute` | **Merged** into `mute` | | ||
| | `switchToMainCall` | `switchToMainCall` | Same | | ||
| | `switchToConsult` | `switchToConsult` | Same | | ||
| | `wrapup` | `wrapup` | Same | | ||
|
|
||
| ### Removed State Flags | ||
| The following state flags were returned by `getControlsVisibility()` but are no longer needed: | ||
|
|
||
| | Old State Flag | Replacement | | ||
| |----------------|-------------| | ||
| | `isConferenceInProgress` | **Do NOT derive from `exitConference.isVisible`** — exit-conference is hidden during conference + active consult (`isConferenceInProgress && !isConsultInitiatedOrAccepted`). Use `task.data.isConferenceInProgress` from task data instead, or check if SDK exposes this as a dedicated flag | | ||
| | `isConsultInitiated` | Derive from `task.uiControls.endConsult.isVisible` if needed | | ||
|
||
| | `isConsultInitiatedAndAccepted` | No longer needed — SDK handles via controls | | ||
| | `isConsultReceived` | No longer needed — SDK handles via controls | | ||
| | `isConsultInitiatedOrAccepted` | No longer needed — SDK handles via controls | | ||
| | `isHeld` | Derive from `task.uiControls.hold` state or SDK task state | | ||
|
||
| | `consultCallHeld` | No longer needed — SDK handles switch controls | | ||
|
|
||
| --- | ||
|
|
||
| ## Refactor Pattern (Before/After) | ||
|
|
||
| ### Before (in `useCallControl` hook) | ||
| ```typescript | ||
| // helper.ts — old approach | ||
| const controls = getControlsVisibility( | ||
| store.deviceType, | ||
| store.featureFlags, | ||
| store.currentTask, | ||
| store.agentId, | ||
| conferenceEnabled, | ||
| store.logger | ||
| ); | ||
|
|
||
| // Pass 22 controls + 7 state flags to component | ||
| return { | ||
| ...controls, | ||
| // additional hook state | ||
| }; | ||
| ``` | ||
|
|
||
| ### After (in `useCallControl` hook) | ||
| ```typescript | ||
| // helper.ts — new approach | ||
| const task = store.currentTask; | ||
| const uiControls = task?.uiControls ?? getDefaultUIControls(); | ||
|
|
||
| // Subscribe to UI control updates | ||
| useEffect(() => { | ||
| if (!task) return; | ||
| const handler = () => { | ||
| // MobX or setState to trigger re-render | ||
| }; | ||
| task.on('task:ui-controls-updated', handler); | ||
| return () => task.off('task:ui-controls-updated', handler); | ||
| }, [task]); | ||
|
|
||
| // Pass SDK-computed controls directly to component | ||
| return { | ||
| controls: uiControls, | ||
| // additional hook state (timers, mute state, etc.) | ||
| }; | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Files to Modify | ||
|
|
||
| | File | Action | | ||
| |------|--------| | ||
| | `task/src/Utils/task-util.ts` | **DELETE** or reduce to `findHoldTimestamp()` only | | ||
| | `task/src/helper.ts` (`useCallControl`) | Remove `getControlsVisibility()` call, use `task.uiControls` | | ||
| | `task/src/task.types.ts` | Import `TaskUIControls` from SDK, remove old control types | | ||
| | `cc-components/src/components/task/task.types.ts` | Align `ControlProps` with new control names | | ||
| | `cc-components/src/components/task/CallControl/call-control.tsx` | Update prop names (`holdResume` → `hold`, etc.) | | ||
| | `cc-components/src/components/task/CallControl/call-control.utils.ts` | Simplify/remove old control derivation | | ||
| | `store/src/task-utils.ts` | Remove `getConsultStatus`, `findHoldStatus` if no longer consumed | | ||
|
||
| | All test files for above | Update to test new contract | | ||
|
|
||
| --- | ||
|
|
||
| ## Validation Criteria | ||
|
|
||
| - [ ] All 17 SDK controls map correctly to widget UI buttons | ||
| - [ ] No widget-side computation of control visibility remains | ||
| - [ ] `task:ui-controls-updated` event drives re-renders | ||
| - [ ] All existing call control scenarios work identically (hold, consult, transfer, conference, wrapup) | ||
| - [ ] Digital channel controls work (accept, end, transfer, wrapup only) | ||
| - [ ] Default controls shown when no task is active | ||
| - [ ] Error boundary still catches failures gracefully | ||
|
|
||
| --- | ||
|
|
||
| _Parent: [001-migration-overview.md](./001-migration-overview.md)_ | ||
Uh oh!
There was an error while loading. Please reload this page.