# Handoff, consultation and specialist responsibility

## What it does

Step 7 separates three responsibilities: hosting a consultation, owning a purchase journey, and completing a specialist task. An offer does not transfer ownership. A recipient must explicitly accept; an email, notification, automatic closer routing or calendar assignment is never acceptance.

The customer’s CJE remains responsible while an ownership offer is pending. An accepted conditional transfer waits for its exact activation condition. Once it activates, the advisor owns the journey and the previous owner remains coordinator. Specialist work changes the execution clock, without changing the journey owner.

## How it works

### Commands and custody

`HandoffWorkflowRepository` uses the existing `JourneyCommandRepository`: customer lock first, context lock second, current permission/version checks, deterministic command receipt, immutable journal and synchronous snapshot in one transaction. Existing `revenue_handoffs` stores the current projection. No new schema or silent lead access grants are needed.

The command envelope is `journey_handoff`. Every command has `request_id`, `expected_version` and `reason`. Actions are:

| Action | Additional fields | Behavior |
|---|---|---|
| Request | kind, to_user_uuid, purpose, activation_condition, optional commitment_uuid; host also appointment_uuid + source_uuid | Freeze recipient and exact task/appointment. Acceptance due in 24 hours. |
| Accept / Reject | handoff_uuid | Only the named, currently eligible recipient responds. |
| Cancel | handoff_uuid | Current scoped journey manager cancels pending or accepted unactivated responsibility. |
| Escalate | handoff_uuid | Explicit in-app audit after 24 hours pending. No external send occurs in this command. |
| Rebook | handoff_uuid, appointment_uuid, source_uuid | Reuses the same host offer for the same appointment and current consultation, preserving the previous response in history; requires a new host response. |
| Complete specialist work | handoff_uuid, follow_up, follow_up_at | Saves summary in `reason`, returns the primary action to its owner with an exact future deadline; does not write readiness facts or customer agreement. |
| Record attendance | handoff_uuid, channel, started_at, ended_at | Accepted host attests actual consultation presence and times; creates a labelled manual source, distinct from the later conversation outcome. |

Host/ownership recipients require advisor assessment and journey management capability plus existing customer access. Specialist recipients require advisor or financing-review capability plus existing customer access. Inactive or merged staff cannot receive or accept work. Recipient acceptance never expands customer or channel permissions.

### Activation and clocks

`HandoffActivationRepository::reconcile()` runs under the same customer lock before snapshot evaluation. Immediate acceptance activates the explicitly selected ownership condition. Qualifying-consultation activation requires a reached advisor outcome or validated attendance in the bound consultation’s reschedule lineage. The advisor must be the named recipient. A webinar, scheduled duration, no-answer or unrelated historical consultation cannot activate ownership. Named-commitment acceptance requires the same recipient’s valid accepted host responsibility for that exact consultation.

The primary commitment’s owner changes atomically with journey ownership. Its original deadline stays unchanged. Every clock transfer records from/to actor, original/current deadline and whether the prior deadline was already missed. Pending/rejected/invalid specialist acceptance leaves the owner’s clock in place. Only an accepted task for the current primary and named specialist changes it. Completion records the specialist result, returns the clock to the owner, and retains the original deadline history.

### Booking transition

Inside the same locked booking transaction, `cancelForBooking()` requires the canonical booking link to this purchase and current journey-management access. It cancels pending and accepted acquisition responsibilities, including ownership waiting on an earlier consultation. Activated/completed history and acceptance receipts remain intact. Each cancellation records its previous state and booking in the audit journal. Repeating the helper has no effect. The helper neither writes customer facts nor changes the executor/deadline: unfinished work continues through the explicit Sales follow-up transfer.

### Consultation order and outcomes

Host acceptance may precede customer agreement. Appointment stage still requires both, a current matching scheduled appointment and Need at least Developing. Either event can arrive second. A cancelled/no-show appointment stops supporting Appointment immediately; a previous completed consultation continues to support Consulted. Rebooking resets host acceptance against the new schedule without duplicating the host offer or transferring ownership.

The existing Zoom meeting duration is scheduled, not measured attendance. It cannot start an outcome clock. An accepted host can record actual start/end/channel attendance with an explicit attestation. Zoom requires at least 10 minutes, phone at least one minute; showroom attendance is advisor attested. The manual source remains labelled as such, never measured telemetry. The real end time starts a 24-hour outcome deadline. A reached outcome note for the same consultation and advisor closes that pending requirement; staff do not have to attest the duration twice. Attendance itself adds no readiness assertions. If the legacy appointment is marked Attended first, the frozen accepted receipt and current appointment/host/time/access still permit the host to write notes; cancelled, no-show, moved, deleted or revoked bindings do not.

### Presentation and access

`HandoffContextLoader` supplies engine handoff facts and an explicit safe `handoff_workspace` DTO. It exposes pending counts, accepted specialist clock owner, outcome-pending deadlines, and current actions. `HandoffWorkspace::options()` runs only on detail views and filters staff by role, capability and existing customer access. Raw handoff models and evidence reference payloads are not serialized. My work includes current eligible recipients with pending offers, accepted conditional ownership, current accepted specialist/host tasks, or a validated pending host outcome. This visibility is independent of the write-release flag. Closed/cancelled requests do not keep a recipient in My work. The queue receives a compact responsibility label and counts rather than all handoff history.

The queue and drawer surface escalations in app. Telegram/email manager delivery remains part of the later notification/automation rollout; recording an escalation does not claim that a message was delivered.

## Related files

- `app/Http/Requests/Manage/RevenueJourney/HandoffRequest.php`
- `src/RevenueJourney/Repositories/HandoffWorkflowRepository.php`
- `src/RevenueJourney/Repositories/HandoffActivationRepository.php`
- `src/RevenueJourney/Repositories/HandoffContextLoader.php`
- `src/RevenueJourney/Repositories/HandoffSupport.php`
- `src/RevenueJourney/Services/HandoffWorkspace.php`
- Existing `JourneyHandoff`, `JourneyCommitment`, `JourneyEvent`, `JourneyCommandRepository`, `WorkflowContextLoader`, `WorkflowSnapshotRepository`, `AppointmentContextLoader`.
- `tests/Feature/RevenueJourney/HandoffWorkflowTest.php`

## Reference usage

The journey controller maps each `HandoffRequest` field explicitly to `journey_handoff` and calls `HandoffWorkflowRepository::execute()`. `WorkflowContextLoader` supplies the current authorized sources before appending handoff facts. `WorkflowSnapshotRepository` runs activation reconciliation and reloads the input only when an accepted transfer activates. Read-only queue rendering never accepts or activates a responsibility.
