# Action Plan Workflow Hardening Implementation Plan

> **READ FIRST — [`2026-08-06-plan-review-corrections.md`](../specs/2026-08-06-plan-review-corrections.md) is a BINDING addendum to this document.** Five independent reviewers found defects that were then verified against the repository. Where the addendum conflicts with anything below, the addendum wins.

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Make every AI-generated follow-up step independently reviewable, assignable, prioritised, actionable and auditable while preserving the existing safe approval/checklist workflow.

**Architecture:** Keep the 1:1 `ZoomMeetingActionPlan` and its JSON review snapshot, but expand each item with validated metadata and per-step assignee IDs. Approval still materialises normal `LeadActionItem` rows in one locked transaction. AI revisions are a separate proposal-only endpoint; Dashboard team data is lazy-loaded and role-scoped.

**Tech Stack:** Laravel/PHP, Eloquent/MySQL, Vue 3/Inertia, Vitest, PHPUnit, existing `AiClient`, existing Lead visibility and Action Item repositories.

## Global Constraints

- Read `docs/superpowers/specs/2026-08-06-ai-sales-follow-up-workspace-design.md` completely before editing.
- Work on an isolated task branch/worktree based on the current local `dev-chen`; integrate verified task commits back into local `dev-chen` only.
- Do not push and do not open a PR.
- `features.zoom_action_plan_demo` remains false by default and gates every new endpoint/prop/control.
- No due-date column. Rename user-facing “Today's Checklist” to “My Checklist”; Current Tasks means every open assigned item.
- No automatic approval, assignment, customer contact, prompt tuning or persisted AI revision.
- Add no request-rate limit in this scope.
- Configure new prompt keys locally through the existing AI Prompt pinning mechanism to OpenAI `gpt-5.6-terra`; never hard-code provider/model in request controllers and never commit the local prompt-pin row.
- Database enums use unsigned integers plus model constants/metadata arrays.
- Preserve `meeting_report.next_steps` as strings; add `recommended_actions` without breaking Call/F2F/Zoom consumers.
- TDD each task; run its focused PHP and JS tests before its commit.
- Each task receives a fresh code review; database/security tasks also receive database/security review, and Vue tasks receive TypeScript/JavaScript review.
- Do not commit production customer data, transcripts, prompt responses, credentials, `.env`, screenshots or browser artifacts.

---

## File map

**Domain and schema**

- Create `database/migrations/2026_08_06_100001_add_workflow_metadata_to_lead_action_items.php` — nullable generated-task priority/type fields.
- Create `database/migrations/2026_08_06_100002_add_snapshots_to_lead_action_item_feedback.php` — rating-time evidence snapshots.
- Modify `src/Lead/LeadActionItem.php` — integer priority/type constants, casts and serialized fields.
- Modify `src/Lead/LeadActionItemFeedback.php` — snapshot fillable/casts.
- Create `src/Conversation/Support/ConversationAnalysisHash.php` — deterministic canonical analysis hash used by feedback provenance.
- Modify `src/Conversation/ConversationAnalysis.php` — additive recommended-action normalization.
- Modify `src/Conversation/Concerns/HasConversationPerformance.php` — normalized recommended actions with legacy fallback.
- Modify `resources/prompts/conversation_analysis.md` — additive JSON contract and evidence rules.

**Action Plan backend**

- Modify `src/Zoom/Services/ZoomActionPlanDraftService.php` — draft rows with AI metadata and default per-step assignee.
- Modify `src/Zoom/Repositories/ZoomMeetingActionPlanRepository.php` — format/validate per-step metadata and materialise it.
- Modify `app/Http/Requests/Manage/Zoom/ActionPlans/UpdateRequest.php` — per-row validation.
- Modify `app/Http/Controllers/Manage/Zoom/ZoomActionPlansController.php` — UUID boundary mapping.
- Create `app/Http/Requests/Manage/Zoom/ActionPlans/SuggestionRequest.php` — bounded revision request.
- Create `app/Http/Controllers/Manage/Zoom/ActionPlanSuggestionController.php` — proposal-only AI endpoint.
- Create `src/Zoom/Services/ActionPlanSuggestionContextBuilder.php` — current unsaved rows plus meeting snapshot.
- Create `resources/prompts/action_plan_revision.md` and register `PROMPT_ACTION_PLAN_REVISION`.
- Modify `routes/web.php` — one flag/auth-gated suggestion route.

**Action Plan frontend**

- Modify `resources/js/composables/useActionPlanEditor.js` and test — full row state/default apply/proposal apply.
- Modify `resources/js/Components/Zoom/ActionPlanReviewModal.vue` — per-row controls and revision preview.
- Create `resources/js/Components/Zoom/ActionPlanRevisionPreview.vue` — current/proposed diff with explicit Apply.

**Feedback**

- Modify `src/Lead/Repositories/LeadActionItemRepository.php` — snapshot upsert.
- Modify `app/Http/Controllers/Manage/Leads/ActionItemsController.php` — map snapshot values server-side.
- Modify `app/Http/Controllers/Manage/Zoom/ActionPlanFeedbackController.php` and `resources/js/Pages/Manage/Zoom/ActionPlans/Feedback.vue` — show rated snapshot/source metadata.

**Checklist and contact actions**

- Modify `app/Http/Controllers/Manage/DashboardController.php` — priority payload, lazy Super Admin team endpoint, stranded count.
- Create `app/Http/Requests/Manage/Dashboard/TeamChecklistRequest.php` — filters/cursor validation.
- Modify `resources/js/Pages/Manage/Dashboard.vue` and `resources/js/Components/TodaysChecklistModal.vue` (keep filename; rename visible copy) — My/Team workspace.
- Create `resources/js/composables/useLeadContactActions.js` and test — shared safe call/WhatsApp handoff.
- Modify `resources/js/Components/Sales/LeadCell.vue` — consume the shared contact composable.
- Modify `routes/web.php` — team checklist route.

---

### Task 1: Add model-backed priority, type and feedback snapshot columns

**Files:**
- Create: `database/migrations/2026_08_06_100001_add_workflow_metadata_to_lead_action_items.php`
- Create: `database/migrations/2026_08_06_100002_add_snapshots_to_lead_action_item_feedback.php`
- Modify: `src/Lead/LeadActionItem.php`
- Modify: `src/Lead/LeadActionItemFeedback.php`
- Test: `tests/Feature/Zoom/ZoomActionPlanFoundationTest.php`
- Test: `tests/Feature/Manage/Leads/ActionItemFeedbackTest.php`

**Interfaces:**
- Produces: `LeadActionItem::PRIORITY_HIGH|MEDIUM|LOW`, `PRIORITIES`, `TYPE_*`, `TYPES`.
- Produces nullable integer columns `suggested_priority`, `priority`, `action_type`.
- Produces feedback snapshots `body_snapshot`, `suggested_priority_snapshot`, `priority_snapshot`, `action_type_snapshot`, `analysis_hash_snapshot`.

- [ ] **Step 1: Write failing schema/model tests**

Add assertions that the new columns exist, constants are unsigned-integer-compatible, casts return integers, and legacy rows accept null metadata. Add a feedback test proving body/priority/type/hash snapshots survive source-item edits and soft deletion.

```php
$item = $this->createGeneratedActionItem([
    'suggested_priority' => LeadActionItem::PRIORITY_HIGH,
    'priority' => LeadActionItem::PRIORITY_MEDIUM,
    'action_type' => LeadActionItem::TYPE_WHATSAPP,
]);

$this->assertSame(LeadActionItem::PRIORITY_MEDIUM, $item->priority);
$this->assertSame('Medium', LeadActionItem::PRIORITIES[$item->priority]['name']);
```

- [ ] **Step 2: Run focused tests and verify failure**

Run:

```bash
php artisan test tests/Feature/Zoom/ZoomActionPlanFoundationTest.php tests/Feature/Manage/Leads/ActionItemFeedbackTest.php
```

Expected: FAIL because constants/columns do not exist.

- [ ] **Step 3: Add the migrations and constants**

Use nullable columns so old/manual Action Items are unchanged:

```php
$table->unsignedInteger('suggested_priority')->nullable()->index()->after('status');
$table->unsignedInteger('priority')->nullable()->index()->after('suggested_priority');
$table->unsignedInteger('action_type')->nullable()->index()->after('priority');
```

Use constants:

```php
public const PRIORITY_HIGH = 1;
public const PRIORITY_MEDIUM = 2;
public const PRIORITY_LOW = 3;

public const TYPE_CALL = 1;
public const TYPE_WHATSAPP = 2;
public const TYPE_SEND_INFORMATION = 3;
public const TYPE_SCHEDULE = 4;
public const TYPE_INTERNAL = 5;
public const TYPE_OTHER = 6;
```

The migration `down()` drops only the columns created by that migration. Add integer casts and fillable fields explicitly.

- [ ] **Step 4: Run migration round-trip and tests**

Run the project scratch-database migration procedure, then the focused tests. Expected: migrations up/down succeed and tests PASS.

- [ ] **Step 5: Review and commit**

Require database review for reversible migrations, indexes, null behavior and integer constants. Fix findings, then commit:

```bash
git add database/migrations/2026_08_06_100001_add_workflow_metadata_to_lead_action_items.php database/migrations/2026_08_06_100002_add_snapshots_to_lead_action_item_feedback.php src/Lead/LeadActionItem.php src/Lead/LeadActionItemFeedback.php tests/Feature/Zoom/ZoomActionPlanFoundationTest.php tests/Feature/Manage/Leads/ActionItemFeedbackTest.php
git commit -m "feat(leads): add action item workflow metadata"
```

### Task 2: Add backward-compatible AI recommended actions

**Files:**
- Modify: `resources/prompts/conversation_analysis.md`
- Modify: `src/Conversation/ConversationAnalysis.php`
- Modify: `src/Conversation/Concerns/HasConversationPerformance.php`
- Test: `tests/Feature/Conversation/ConversationAnalyzerTest.php`
- Test: `tests/Feature/Zoom/ZoomActionPlanDraftTest.php`

**Interfaces:**
- Produces: `ConversationAnalysis::PRIORITIES`, `ACTION_TYPES`.
- Produces: `HasConversationPerformance::recommendedActionItems(): array<int,array{body:string,priority:string,action_type:string,reason:?string}>`.
- Preserves: `actionItems(): array<int,string>` and every existing `next_steps` consumer.

- [ ] **Step 1: Write failing normalizer and fallback tests**

Cover valid rows, invalid enum clamping, nested/scalar garbage, maximum list length, missing field and a legacy analysis with only `next_steps`.

```php
$analysis = ConversationAnalysis::normalize([
    'meeting_report' => [
        'next_steps' => ['Send comparison'],
        'recommended_actions' => [[
            'body' => 'Send comparison',
            'priority' => 'urgent',
            'action_type' => 'magic',
            'reason' => 'Financing concern',
        ]],
    ],
]);

$this->assertSame('medium', $analysis['meeting_report']['recommended_actions'][0]['priority']);
$this->assertSame('other', $analysis['meeting_report']['recommended_actions'][0]['action_type']);
```

- [ ] **Step 2: Run focused tests and verify failure**

```bash
php artisan test tests/Feature/Conversation/ConversationAnalyzerTest.php tests/Feature/Zoom/ZoomActionPlanDraftTest.php
```

Expected: FAIL because recommended actions are currently discarded inside the core `meeting_report` shape.

- [ ] **Step 3: Implement the additive prompt contract and normalizer**

Keep `next_steps` unchanged and add:

```json
"recommended_actions": [{
  "body": "a concrete assignable action",
  "priority": "high | medium | low",
  "action_type": "call | whatsapp | send_information | schedule | internal | other",
  "reason": "one short evidence-based reason"
}]
```

Prompt rules must say priority is urgency/importance, not conversion probability; reason must cite recorded needs/concerns/commitments; unknown evidence means omit the action rather than invent it. Normalize at most 10 non-empty rows.

Implement a private `recommendedActions()` sanitizer and public trait method. When the array is empty, the trait maps legacy `next_steps` to Medium/Other/null-reason rows without mutating stored analysis.

- [ ] **Step 4: Run regression tests for all three channels**

```bash
php artisan test tests/Feature/Conversation/ConversationAnalyzerTest.php tests/Feature/Zoom/ZoomActionPlanDraftTest.php tests/Feature/Manage/Calls tests/Feature/Manage/F2f
```

Expected: PASS; existing string action items remain unchanged.

- [ ] **Step 5: Review and commit**

Require AI-output normalization and backward-compatibility review. Commit:

```bash
git add resources/prompts/conversation_analysis.md src/Conversation/ConversationAnalysis.php src/Conversation/Concerns/HasConversationPerformance.php tests/Feature/Conversation/ConversationAnalyzerTest.php tests/Feature/Zoom/ZoomActionPlanDraftTest.php
git commit -m "feat(ai): suggest typed action plan steps"
```

### Task 3: Build and save per-step review metadata

**Files:**
- Modify: `src/Zoom/Services/ZoomActionPlanDraftService.php`
- Modify: `src/Zoom/Repositories/ZoomMeetingActionPlanRepository.php`
- Modify: `app/Http/Requests/Manage/Zoom/ActionPlans/UpdateRequest.php`
- Modify: `app/Http/Controllers/Manage/Zoom/ZoomActionPlansController.php`
- Test: `tests/Feature/Zoom/ZoomActionPlanDraftTest.php`
- Test: `tests/Feature/Manage/Zoom/ZoomActionPlansEndpointsTest.php`

**Interfaces:**
- HTTP item: `{key,body,reason,suggested_priority,priority,action_type,assignee_uuid}`.
- Stored item: same semantic values, with integer `assignee_id` and integer enum values.
- Existing plan `assignee_id` remains the default-assignee field.

- [ ] **Step 1: Write failing draft and endpoint tests**

Cover AI metadata, legacy defaults, one assignee per row, plan-level default, forged/ineligible assignee, invalid priority/type, reanalysis replacement, and approved snapshot immutability.

- [ ] **Step 2: Verify red tests**

```bash
php artisan test tests/Feature/Zoom/ZoomActionPlanDraftTest.php tests/Feature/Manage/Zoom/ZoomActionPlansEndpointsTest.php
```

Expected: FAIL on missing row metadata and request rules.

- [ ] **Step 3: Update draft construction and server formatting**

Map codes with model constants and build rows:

```php
$items[] = [
    'key' => $key,
    'body' => $action['body'],
    'reason' => $action['reason'],
    'suggested_priority' => LeadActionItem::priorityFromCode($action['priority']),
    'priority' => LeadActionItem::priorityFromCode($action['priority']),
    'action_type' => LeadActionItem::typeFromCode($action['action_type']),
    'assignee_id' => $defaultAssigneeId,
];
```

Do not write on GET. Upgrade legacy rows only in serializer memory and on the next explicit PUT.

- [ ] **Step 4: Validate/map every HTTP field explicitly**

Add per-item rules, but resolve eligible users and enum constants again in the repository. `show()` maps stored `assignee_id` to UUID using an eager-loaded eligible-user map. `mapPlanInput()` maps fields one by one; never pass `validated()` wholesale.

- [ ] **Step 5: Run tests, review and commit**

```bash
php artisan test tests/Feature/Zoom/ZoomActionPlanDraftTest.php tests/Feature/Manage/Zoom/ZoomActionPlansEndpointsTest.php
```

Require security review of UUID/Lead visibility checks, then commit:

```bash
git add src/Zoom/Services/ZoomActionPlanDraftService.php src/Zoom/Repositories/ZoomMeetingActionPlanRepository.php app/Http/Requests/Manage/Zoom/ActionPlans/UpdateRequest.php app/Http/Controllers/Manage/Zoom/ZoomActionPlansController.php tests/Feature/Zoom/ZoomActionPlanDraftTest.php tests/Feature/Manage/Zoom/ZoomActionPlansEndpointsTest.php
git commit -m "feat(zoom): review action plan steps independently"
```

### Task 4: Approve independently assigned and prioritised steps

**Files:**
- Modify: `src/Zoom/Repositories/ZoomMeetingActionPlanRepository.php`
- Modify: `src/Lead/Repositories/LeadActionItemRepository.php`
- Test: `tests/Feature/Zoom/ZoomActionPlanApprovalTest.php`

**Interfaces:**
- Consumes the stored per-step contract from Task 3.
- Produces one `LeadActionItem` per row with its own pivot assignee and metadata.

- [ ] **Step 1: Write failing approval tests**

Cover mixed assignees, mixed priorities/types, one invalid row rolling back all rows, missing row assignee, double approval, simulated concurrency, unchanged notifier behavior and legacy draft approval.

- [ ] **Step 2: Verify failures**

```bash
php artisan test tests/Feature/Zoom/ZoomActionPlanApprovalTest.php
```

- [ ] **Step 3: Implement transaction-wide validation before writes**

Resolve all row assignees and enum values into an in-memory `$resolvedSteps` array before calling `createGeneratedItems()`. Only after every row is valid may the repository write:

```php
$data['lead_action_item'] = [
    'lead_id' => $lead->id,
    'body' => $step['body'],
    'suggested_priority' => $step['suggested_priority'],
    'priority' => $step['priority'],
    'action_type' => $step['action_type'],
    'source_action_plan_id' => $plan->id,
    'source_step_key' => $step['key'],
];
$data['assignee_ids'] = [$step['assignee']->id];
```

Retain the row lock, transaction and unique index.

- [ ] **Step 4: Run tests and review transaction safety**

Expected: all approval tests PASS. Require database/security review for partial-write, forged UUID and idempotency paths.

- [ ] **Step 5: Commit**

```bash
git add src/Zoom/Repositories/ZoomMeetingActionPlanRepository.php src/Lead/Repositories/LeadActionItemRepository.php tests/Feature/Zoom/ZoomActionPlanApprovalTest.php
git commit -m "feat(zoom): assign and prioritise approved steps"
```

### Task 5: Upgrade the review editor UI

**Files:**
- Modify: `resources/js/composables/useActionPlanEditor.js`
- Modify: `resources/js/composables/useActionPlanEditor.test.js`
- Modify: `resources/js/Components/Zoom/ActionPlanReviewModal.vue`
- Test: existing Vitest suite plus focused component coverage where the project pattern supports it.

**Interfaces:**
- Editor exposes `defaultAssigneeUuid`, `applyDefaultAssignee()`, `updateStep(index, patch)` and `replaceWithProposal(items)`.
- `payload()` exactly matches Task 3's HTTP item contract.

- [ ] **Step 1: Write failing composable tests**

Prove init/defaults, row override, Apply to all, reorder identity, proposal replacement, invalid/empty rows and payload trimming. “Apply to all” is explicit; changing the default selector alone changes no row.

- [ ] **Step 2: Run Vitest red**

```bash
npx vitest run resources/js/composables/useActionPlanEditor.test.js
```

- [ ] **Step 3: Implement the pure editor contract**

Keep client-only `id` out of payload. `updateStep` accepts an allow-listed patch:

```js
const updateStep = (index, patch) => {
    const allowed = ['body', 'priority', 'action_type', 'assignee_uuid'];
    steps.value = steps.value.map((step, i) => i === index
        ? { ...step, ...Object.fromEntries(Object.entries(patch).filter(([key]) => allowed.includes(key))) }
        : step);
};
```

- [ ] **Step 4: Render row controls and AI-vs-human labels**

Each row shows reason, suggested badge, confirmed priority, action type and assignee. Preserve approved plans as read-only. Approval disables until every non-empty row has valid selections.

- [ ] **Step 5: Run JS tests/build, review and commit**

```bash
npx vitest run resources/js/composables/useActionPlanEditor.test.js
npm run build
```

Require Vue/accessibility review. Commit:

```bash
git add resources/js/composables/useActionPlanEditor.js resources/js/composables/useActionPlanEditor.test.js resources/js/Components/Zoom/ActionPlanReviewModal.vue
git commit -m "feat(zoom): add per-step action plan controls"
```

### Task 6: Add proposal-only AI revisions

**Files:**
- Create: `resources/prompts/action_plan_revision.md`
- Modify: `config/ai_prompts.php`
- Modify: `src/Ai/AiRequest.php`
- Create: `src/Zoom/Services/ActionPlanSuggestionContextBuilder.php`
- Create: `app/Http/Requests/Manage/Zoom/ActionPlans/SuggestionRequest.php`
- Create: `app/Http/Controllers/Manage/Zoom/ActionPlanSuggestionController.php`
- Modify: `routes/web.php`
- Create: `resources/js/Components/Zoom/ActionPlanRevisionPreview.vue`
- Modify: `resources/js/Components/Zoom/ActionPlanReviewModal.vue`
- Test: `tests/Feature/Zoom/ActionPlanSuggestionContextBuilderTest.php`
- Test: `tests/Feature/Manage/Zoom/ActionPlanSuggestionTest.php`

**Interfaces:**
- POST body: `{instruction:string, items:[current HTTP item rows]}`.
- JSON response: `{proposal:{items:[HTTP item rows]}, context:{transcript:{used,total,truncated}}}`.
- No database write except normal `ai_requests` observability.

- [ ] **Step 1: Write failing context, auth and no-write tests**

Cover flag 404, non-Super Admin 403, bounded instruction/rows, transcript truncation honesty, prompt injection delimiter protection, invalid model JSON, unknown/duplicated keys, provider failure and database unchanged.

- [ ] **Step 2: Verify failures**

```bash
php artisan test tests/Feature/Zoom/ActionPlanSuggestionContextBuilderTest.php tests/Feature/Manage/Zoom/ActionPlanSuggestionTest.php
```

- [ ] **Step 3: Implement JSON-only proposal generation**

Reuse `ZoomRecordingChatContextBuilder`; treat transcript, analysis, reviewer rows and instruction as quoted untrusted data. Prompt must return the complete proposed list and may only use submitted assignee UUIDs/enum codes. The server allow-lists known step keys and eligible assignees and rejects the entire malformed proposal with 422/503; it never saves.

- [ ] **Step 4: Add explicit preview/apply UI**

Show Current and Proposed. `Apply proposal` calls `editor.replaceWithProposal()` only. Closing/retrying leaves the editor unchanged. Save/Approve remain separate buttons.

- [ ] **Step 5: Run tests/build, security review and commit**

```bash
php artisan test tests/Feature/Zoom/ActionPlanSuggestionContextBuilderTest.php tests/Feature/Manage/Zoom/ActionPlanSuggestionTest.php
npm run build
```

Commit:

```bash
git add resources/prompts/action_plan_revision.md config/ai_prompts.php src/Ai/AiRequest.php src/Zoom/Services/ActionPlanSuggestionContextBuilder.php app/Http/Requests/Manage/Zoom/ActionPlans/SuggestionRequest.php app/Http/Controllers/Manage/Zoom/ActionPlanSuggestionController.php routes/web.php resources/js/Components/Zoom/ActionPlanRevisionPreview.vue resources/js/Components/Zoom/ActionPlanReviewModal.vue tests/Feature/Zoom/ActionPlanSuggestionContextBuilderTest.php tests/Feature/Manage/Zoom/ActionPlanSuggestionTest.php
git commit -m "feat(zoom): propose reviewable action plan revisions"
```

### Task 7: Preserve useful step-feedback evidence

**Files:**
- Create: `src/Conversation/Support/ConversationAnalysisHash.php`
- Modify: `src/Lead/Repositories/LeadActionItemRepository.php`
- Modify: `app/Http/Controllers/Manage/Leads/ActionItemsController.php`
- Modify: `app/Http/Controllers/Manage/Zoom/ActionPlanFeedbackController.php`
- Modify: `resources/js/Pages/Manage/Zoom/ActionPlans/Feedback.vue`
- Test: `tests/Feature/Manage/Leads/ActionItemFeedbackTest.php`

**Interfaces:**
- Snapshot fields come from the server-loaded item/plan, never browser input.
- `ConversationAnalysisHash::make(array $analysis): string` recursively key-sorts associative arrays, preserves list order, encodes with `JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR`, then returns SHA-256.

- [ ] **Step 1: Write failing snapshot and authorization tests**

Prove snapshot accuracy, re-rating refresh, source edit/delete survival, legacy null metadata, flag gate and assignee-only rating.

- [ ] **Step 2: Verify failures**

```bash
php artisan test tests/Feature/Manage/Leads/ActionItemFeedbackTest.php
```

- [ ] **Step 3: Implement server-authored snapshot upsert**

Map fields under `lead_action_item_feedback` inside the controller/repository. Never accept snapshot fields from the Form Request.

- [ ] **Step 4: Show snapshots and honest AI attribution**

The admin listing shows rated text/priority/type even if the live item changed. If displaying an `ai_requests` row, label it “latest analysis request for this meeting”; do not call it an exact prompt version.

- [ ] **Step 5: Test, review and commit**

```bash
php artisan test tests/Feature/Manage/Leads/ActionItemFeedbackTest.php
npm run build
```

```bash
git add src/Conversation/Support/ConversationAnalysisHash.php src/Lead/Repositories/LeadActionItemRepository.php app/Http/Controllers/Manage/Leads/ActionItemsController.php app/Http/Controllers/Manage/Zoom/ActionPlanFeedbackController.php resources/js/Pages/Manage/Zoom/ActionPlans/Feedback.vue tests/Feature/Manage/Leads/ActionItemFeedbackTest.php
git commit -m "feat(leads): snapshot ai step feedback context"
```

### Task 8: Turn the personal checklist into a role-aware workspace

**Files:**
- Create: `app/Http/Requests/Manage/Dashboard/TeamChecklistRequest.php`
- Modify: `app/Http/Controllers/Manage/DashboardController.php`
- Modify: `routes/web.php`
- Modify: `resources/js/Pages/Manage/Dashboard.vue`
- Modify: `resources/js/Components/TodaysChecklistModal.vue`
- Modify: `resources/js/utils/actionPlanChecklist.js`
- Modify: `resources/js/utils/actionPlanChecklist.test.js`
- Test: `tests/Feature/Manage/DashboardChecklistTest.php`

**Interfaces:**
- Existing initial `actionPlanChecklist` remains the signed-in user's own open list.
- New GET `/manage/dashboard/checklist/team` accepts `assignee`, `priority`, `cursor`; Super Admin only.
- Team response: `{items:[{item,lead,plan,assignee,priority,action_type}],summary:{open,stranded},next_cursor}`.

- [ ] **Step 1: Write failing own/team/auth/filter/order tests**

Cover Sales isolation, Super Admin team access, non-Super 403, flag 404, Lead visibility, priority order, pagination, completed history and stranded rows that are counted without exposure to an ineligible old assignee.

- [ ] **Step 2: Verify failures**

```bash
php artisan test tests/Feature/Manage/DashboardChecklistTest.php
```

- [ ] **Step 3: Add priority payload and lazy Team query**

Keep the landing query scoped to the current user. The team endpoint is separate, paginated and uses `LeadVisibility::apply()` plus Super Admin authorization. Order `priority ASC` (High=1) then plan approval/id descending.

- [ ] **Step 4: Update copy and tabs**

Visible title becomes `My Checklist`. Sales sees Current Tasks / Completed History. Super Admin additionally sees My Tasks / Team Tasks, with Team loaded only after selection. Show priority/type badges and assignee.

- [ ] **Step 5: Run PHP/JS/build, review and commit**

```bash
php artisan test tests/Feature/Manage/DashboardChecklistTest.php
npx vitest run resources/js/utils/actionPlanChecklist.test.js
npm run build
```

```bash
git add app/Http/Requests/Manage/Dashboard/TeamChecklistRequest.php app/Http/Controllers/Manage/DashboardController.php routes/web.php resources/js/Pages/Manage/Dashboard.vue resources/js/Components/TodaysChecklistModal.vue resources/js/utils/actionPlanChecklist.js resources/js/utils/actionPlanChecklist.test.js tests/Feature/Manage/DashboardChecklistTest.php
git commit -m "feat(manage): add role-aware sales checklist workspace"
```

### Task 9: Add safe contact actions for typed tasks

**Files:**
- Create: `resources/js/composables/useLeadContactActions.js`
- Create: `resources/js/composables/useLeadContactActions.test.js`
- Modify: `resources/js/Components/Sales/LeadCell.vue`
- Modify: `resources/js/Components/TodaysChecklistModal.vue`
- Modify: `app/Http/Controllers/Manage/DashboardController.php`
- Test: `tests/Feature/Manage/DashboardChecklistTest.php`

**Interfaces:**
- `dial({leadUuid, phone, name})` logs with keepalive then sets `window.location.href = tel:`.
- `whatsapp({phone})` opens normalized `wa.me` with `noopener`.
- Checklist Lead payload includes `phone` only after Lead visibility has been applied.

- [ ] **Step 1: Write failing JS and payload tests**

Cover call log order, keepalive, cancel/no-call, WhatsApp digit normalization, invalid phone, action-type button visibility and no raw phone in unauthorized/team rows.

- [ ] **Step 2: Run red tests**

```bash
npx vitest run resources/js/composables/useLeadContactActions.test.js resources/js/Components/Sales/LeadCell.test.js
php artisan test tests/Feature/Manage/DashboardChecklistTest.php
```

- [ ] **Step 3: Extract and reuse the contact handoff**

Move the existing `LeadCell.vue` call-intent behavior without changing its observable contract. WhatsApp opens a new tab. Do not infer action type from task body.

- [ ] **Step 4: Render task-specific controls and verify**

Call button only for `TYPE_CALL`, WhatsApp only for `TYPE_WHATSAPP`, and neither when the phone is absent/invalid.

- [ ] **Step 5: Review and commit**

```bash
npx vitest run resources/js/composables/useLeadContactActions.test.js resources/js/Components/Sales/LeadCell.test.js
php artisan test tests/Feature/Manage/DashboardChecklistTest.php
npm run build
```

```bash
git add resources/js/composables/useLeadContactActions.js resources/js/composables/useLeadContactActions.test.js resources/js/Components/Sales/LeadCell.vue resources/js/Components/TodaysChecklistModal.vue app/Http/Controllers/Manage/DashboardController.php tests/Feature/Manage/DashboardChecklistTest.php
git commit -m "feat(manage): add checklist contact actions"
```

### Task 10: Complete workflow verification and local integration

**Files:**
- Modify only tests/docs needed to correct defects found during verification.

- [ ] **Step 1: Run focused backend suite**

```bash
php artisan test tests/Feature/Conversation/ConversationAnalyzerTest.php tests/Feature/Zoom/ZoomActionPlanFoundationTest.php tests/Feature/Zoom/ZoomActionPlanDraftTest.php tests/Feature/Zoom/ZoomActionPlanApprovalTest.php tests/Feature/Zoom/ActionPlanSuggestionContextBuilderTest.php tests/Feature/Manage/Zoom/ZoomActionPlansEndpointsTest.php tests/Feature/Manage/Zoom/ActionPlanSuggestionTest.php tests/Feature/Manage/Leads/ActionItemFeedbackTest.php tests/Feature/Manage/DashboardChecklistTest.php
```

Expected: all PASS.

- [ ] **Step 2: Run frontend suite and builds**

```bash
npm test
npm run build
```

Expected: tests PASS and client + SSR builds succeed. Record unrelated baseline failures separately; do not label a new regression pre-existing without a clean-base reproduction.

- [ ] **Step 3: Run formatting/static checks**

Run Pint only on changed PHP files first, then the project's normal relevant static checks. Do not reformat unrelated files.

- [ ] **Step 4: Rehearse the acceptance journey in `petav3.test`**

Verify two different row assignees, priority sorting, revision preview/no-write, idempotent approval, Sales isolation, Team Tasks, call/WhatsApp handoff and feedback snapshots. Verify the flag-off state has no new UI/props and dedicated routes 404.

- [ ] **Step 5: Final independent reviews**

Run integration, security, database and Vue reviews over the complete diff. Fix every P0/P1 and justified P2 finding, rerun affected checks, and leave a concise evidence log.

- [ ] **Step 6: Integrate locally**

Ensure both worktrees are clean, create a recoverable backup ref for local `dev-chen`, then cherry-pick only this plan's task commits into the local `dev-chen` worktree. Do not push and do not open a PR.
