Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
439effd
docs(ai-docs): add task refactor migration documentation (old vs new)
akulakum Mar 9, 2026
4a534db
Merge branch 'next' into TASK_REFACTOR_MIGRATION_OLD_VS_NEW
akulakum Mar 10, 2026
45a1bd6
docs(ai-docs): address codex review comments
akulakum Mar 10, 2026
28397c2
docs(ai-docs): address codex second review comments
akulakum Mar 10, 2026
3bd613f
docs(ai-docs): address codex third review comments
akulakum Mar 10, 2026
606dcbf
docs(ai-docs): address codex fourth review comments
akulakum Mar 10, 2026
23c1fab
docs(ai-docs): address codex fifth review comments
akulakum Mar 10, 2026
e4ea922
docs(ai-docs): address codex sixth review comments
akulakum Mar 11, 2026
b35a8c3
Merge branch 'next' into TASK_REFACTOR_MIGRATION_OLD_VS_NEW
akulakum Mar 11, 2026
2313f1a
docs(ai-docs): address codex seventh review comments
akulakum Mar 11, 2026
0024a37
docs(ai-docs): split PR — retain only foundation docs (PR 1/4)
akulakum Mar 11, 2026
abbbaf3
docs(ai-docs): address codex eighth review — fix conference state der…
akulakum Mar 11, 2026
eabc80d
docs(ai-docs): add CC SDK task-refactor branch reference to 001-migra…
akulakum Mar 11, 2026
6e50dc5
docs(ai-docs): address codex ninth review — fix consult mapping, reco…
akulakum Mar 11, 2026
0d08a09
docs(ai-docs): address codex tenth review — add TaskState export, fix…
akulakum Mar 11, 2026
d576676
docs(ai-docs): address codex eleventh review — keep findHoldStatus in…
akulakum Mar 11, 2026
4a28a43
docs(ai-docs): address reviewer feedback — restructure 001 overview, …
akulakum Mar 11, 2026
91dd405
docs(ai-docs): consolidate PR 1 migration docs into single overview
akulakum Mar 12, 2026
319b843
Merge branch 'next' into TASK_REFACTOR_MIGRATION_OLD_VS_NEW
akulakum Mar 17, 2026
86ada36
docs(migration): SDK exports list/timeline, define gotcha
akulakum Mar 17, 2026
4471af5
Merge branch 'next' into TASK_REFACTOR_MIGRATION_OLD_VS_NEW
akulakum Mar 18, 2026
e6ce0da
Merge branch 'next' into TASK_REFACTOR_MIGRATION_OLD_VS_NEW
akulakum Mar 19, 2026
3c5f274
docs(migration): address — reorder sections, add consultTransfer meth…
akulakum Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions ai-docs/migration/001-migration-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# 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 |
| 004 | [004-call-control-hook-migration.md](./004-call-control-hook-migration.md) | Refactor `useCallControl` hook, timer utils, fix bugs | **High** (largest widget) | P0 |
| 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 |
| 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

### 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to update this statement and point towards the new object structure that is being maintained in SDK. Saying that we receive {isVisible, isEnabled} per control is vague considering the change that has been done in SDK

```

**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)
| 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` |

---

## CC SDK Task-Refactor Branch Reference

> **Repo:** [webex/webex-js-sdk (task-refactor)](https://github.com/webex/webex-js-sdk/tree/task-refactor)
> **Local path:** `/Users/akulakum/Documents/CC_SDK/webex-js-sdk` (branch: `task-refactor`)

### Key SDK Source Files

| File | Purpose |
|------|---------|
| `uiControlsComputer.ts` | Computes `TaskUIControls` from `TaskState` + `TaskContext` — the single source of truth for all control visibility/enabled states |
| `constants.ts` | `TaskState` enum (IDLE, OFFERED, CONNECTED, HELD, CONSULT_INITIATING, CONSULTING, CONF_INITIATING, CONFERENCING, WRAPPING_UP, COMPLETED, TERMINATED, etc.) and `TaskEvent` enum |
| `types.ts` | `TaskContext`, `UIControlConfig`, `TaskStateMachineConfig` |
| `TaskStateMachine.ts` | State machine configuration with transitions, guards, and actions |
| `actions.ts` | State machine action implementations |
| `guards.ts` | Transition guard conditions |
| `../Task.ts` | Task service exposing `task.uiControls` getter and `task:ui-controls-updated` event |
| `../TaskUtils.ts` | Shared utility functions used by `uiControlsComputer.ts` (e.g., `getIsConferenceInProgress`, `getIsCustomerInCall`) |

### Key SDK Architectural Decisions

These decisions in the SDK directly impact how the migration docs should be interpreted:

1. **`exitConference` visibility:** In the SDK, `exitConference` is `VISIBLE_DISABLED` (not hidden) during consulting-from-conference. This differs from the old widget logic where it was hidden. `exitConference.isVisible` is therefore more reliable in the new SDK for detecting conference state, but consulted agents not in conferencing state still see `DISABLED`.

2. **`TaskState.CONSULT_INITIATING` vs `CONSULTING`:** The SDK has `CONSULT_INITIATING` (consult requested, async in-progress) and `CONSULTING` (consult accepted, actively consulting) as distinct states. The old widget constant `TASK_STATE_CONSULT` ('consult') maps to `CONSULT_INITIATING`, NOT `CONSULTING`. `TaskState.CONSULT_INITIATED` exists in the enum but is marked "NOT IMPLEMENTED".

3. **Recording control:** `recording.isEnabled = true` when recording is in progress (allows pause/resume toggle). `recording.isEnabled = false` when recording is not active (allows starting). This means paused recordings show `{ isVisible: true, isEnabled: true }` to allow resumption.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep recording control enabled when toggle action is allowed

This guidance says recording.isEnabled = false when recording is not active while also saying that state "allows starting", which conflicts with widget button semantics where isEnabled drives interactivity (disabled: !...isEnabled in cc-components/src/components/task/CallControl/call-control.utils.ts). If implementers follow this literally, the recording toggle becomes non-clickable in non-recording states and agents lose the manual start/resume path; fresh evidence is that this migration set also maps toggle interactivity to recording.isEnabled in Doc 002.

Useful? React with 👍 / 👎.


4. **`isHeld` derivation:** The SDK computes `isHeld` from `serverHold ?? state === TaskState.HELD` (line 81 of `uiControlsComputer.ts`). Hold control can be `VISIBLE_DISABLED` in conference/consulting states without meaning the call is held. Widgets must derive `isHeld` from task data (`findHoldStatus`), not from `controls.hold.isEnabled`.

5. **`UIControlConfig` built internally:** The SDK builds `UIControlConfig` from agent profile, `callProcessingDetails`, media type, and voice variant. Widgets do NOT need to provide it.

6. **Conference state (`inConference`):** The SDK computes `inConference` as `conferenceActive && (isConferencing || selfInMainCall || consultInitiator)` (line 97). This is broader than `isConferencing` state alone, accounting for backend conference flags and consult-from-conference flows.

---

## SDK Version Requirements

The CC Widgets migration depends on the CC SDK `task-refactor` branch being merged and released. Key new APIs:

| 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

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

### 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)_
Loading
Loading