# Staff workspace — Steps 5–6

## What it does

Adds the first **Work queue** tab to AI Copilot, with Queue and Day plan views, a customer drawer, readiness details, a call brief and the Record outcome workflow. Staff can record a dated follow-up, customer agreement, manual evidence and review decisions using the existing Step 4 transaction boundary.

The manual interface was released in Step 5. Step 6 adds automatic source ingestion, scoped activity previews and continuous refresh. A three-user timed acceptance trial remains pending; automated checks are not a substitute for staff acceptance.

## How it works

### Entry and release controls

- GET `/manage/ai-copilot/work?suite=other`; root Copilot redirects here when the workspace flag and `view-journey` permission are present.
- The original Customers view remains accessible through its tab (`legacy=1`). Turning the workspace flag off restores the original landing behavior.
- All routes require authenticated active staff, existing lead access and the workspace flag. Existing lead visibility, context owner/coordinator and field-specific capabilities still apply.
- `JOURNEY_QUEUE` enables the workspace. With `JOURNEY_WRITES=0`, reads remain available and every mutation is denied. Both settings retain their disabled defaults; no role grants are added.
- A scoped manager can add an **existing customer** as an inactive intake or purchase opportunity, then select an eligible owner and dated primary action. Automatic intake import is separately controlled by `journey.ingestion_enabled`; existing CRM owners are preserved and no deadlines or purchase opportunities are invented.

### Read projection

`JourneyWorkspace` evaluates all authorized open contexts before sorting and pagination. Quiet tracked opportunities remain included. Batched context/source reads avoid a query loop per intake; mutations and detail reads still lock and recheck current records. GET performs no writes. `WorkspacePresenter` explicitly allowlists output; raw rule input, provider payloads and AI traces are never serialized.

Channel adapters recheck source identity, current revision, actual timing and viewer access. A current message is shown directly in the row; the activity feed combines Portal, WhatsApp, Email, Zoom, Phone Call and Showroom F2F, plus relevant calendar and secure-process status. Customer activity is separate from purchase applicability. Ambiguous purchase activity requires **Use for this purchase** with an audited reason. See [channel sources](channel-source-contract.md) and [email](email-channel.md).

Readiness uses seven evidence-derived states and four decision gates; intakes show Need/Relationship only. No arbitrary 100-point score or activity-completion score is introduced. Stage dates come from actual stage events; skipped stages are not represented as completed.

### Staff flow

1. Queue shows Why now, customer/purchase, visible customer words, readiness, next action, due date, waiting party and owner. Customer detail has **Follow up** and **Customer journey** sections.
2. Conversation links open a new tab and preserve the active suite. Keyboard focus is trapped on the active surface; Escape closes the inner dialog before the drawer. Mobile uses cards.
3. Day plan shows dated actions, documented obstacles and parked review dates. The default uses Malaysia time (Day before 10:00, Queue afterwards), with the chosen view remembered in browser storage.
4. Dimension details show effective facts, quotes, source/date, pending/reconfirmation status and field-specific inputs. Confirm, reject, correct, reconfirm and documented blocking all use authorized commands. There is no Mark Ready button.
5. Call brief lists missing customer-callable facts; preparation checkboxes are local only.
6. Record outcome closes the current action, saves confirmed facts/reviews, creates the dated successor (or parks/closes), journals and recomputes atomically. A no-answer creates the existing next-working-day retry and does not invent a conversation. Advisor consultation attestation is capability gated.
7. **Preview readiness and next-step suggestion** runs the exact outcome command inside an always-rolled-back transaction, then returns a safe projection. No preview is saved. Staff can apply the resulting suggestion before saving.
8. Typed supporting records include people, statements, advisor assessments, shared funding pools, local-project offers/payment dates, financing routes and cash reservations. The offer picker currently covers authorized **local sales projects**, not the federated catalogue. Shared-pool corrections must reference the existing assertion in its original context. No document upload is added; secure document handling remains the existing system's responsibility.

All dates entered here use Malaysia time and preserve seconds on conversion. Money is submitted as decimal strings. Transport retries preserve request identity; intentional payload changes receive a new identity. A stale-version conflict retains the form draft and requires explicit reconciliation with the refreshed context. Rules and authorization are rechecked at save time.

### Step 6 — channel activity and staff resolution interface

`WorkflowContextLoader::prime()` constructs a request-local `WorkflowContextBatch` for already-authorized contexts. `forRead()` consumes its scoped events, snapshots, evidence/reviews, source matches, contact policies and primary commitments. The locked command/detail path always bypasses the batch. Channel sources still pass current revision and viewer-access checks. The batch uses nine preload queries for both ten and one hundred intakes, with zero additional per-intake loader queries in the bounded regression test; this is a loader measurement, not an end-to-end production latency claim.

The queue shows the latest accessible source text, channel and timestamp directly on each customer row. Source links open in a new tab and retain the current suite. The drawer keeps **Follow up** and **Customer journey** separate; Follow up contains a newest-first activity feed with channel filters for Portal, WhatsApp, Email, Zoom, calls and F2F when those sources are present. Pagination within the feed is local to its disclosed latest activity window.

Customer reply obligations are evaluated independently of purchase applicability. A separate operational source collection cannot contribute to readiness. A same-customer audited reply resolution is shared across their journeys; an edited request reopens. The workspace processes 100 contexts at a time under the existing worker memory limit and sorts all resulting rows before pagination. Empty backing intakes are hidden when an open purchase exists; intakes with real primary work remain visible.

`row.channels` contains `{key,label,count,last_at}`. `selected.activity` contains viewer-safe `{id,channel,channel_key,title,evidence,at,href,kind,direction,attribution,tracking_caveat,can_link,can_resolve}` records, with optional `activity_total`. Only explicit inbound messages with customer-compatible speaker metadata are presented as customer speech; automated and unknown-speaker sources remain neutral. Email subjects and draft/send-state labels stay visible in compact previews. Tracked email opens/clicks carry their evidence limitation inline and never become a confirmed readiness claim. Plain text interpolation is used for every source excerpt; unsafe URL protocols are discarded.

A manager-scoped backend chooses the initial work scope. `scopeCounts` supplies visible, mine and unassigned counts; an empty personal view offers a direct switch to accessible team work. Filtered-empty and initial-sync states explain their different causes. The interface does not tell staff to recreate every customer manually.

When `sync.enabled` is true, the queue checks for fresh props every 60 seconds while visible, and responds to its authorized private Reverb refresh channel. Partial refresh retains selection, filters, scroll and local state. Opening any outcome/evidence/source dialog cancels an in-flight refresh and pauses subsequent checks, preventing background updates from overwriting a draft. Network and HTTP failures show a retry message without opening an error overlay. `generatedAt` is the view time; `sync.last_synced_at` is independently labelled customer-data sync time.

Permission-gated activity actions use `journey_source` with `request_id`, `expected_version`, `source_uuid` and a nonblank reason of at most 500 characters. **Use for this purchase** names the currently selected opportunity and is not available on an intake. **Already handled** records how an incoming message was addressed through another channel. Request identities survive transport retries, intentional changes renew them, and stale-version conflicts retain the note until explicit reconciliation. These actions do not send a customer message.

Related frontend: `Journey/{SignalPreview,ChannelBadge,ActivityFeed,ActivityActionDialog}.vue`, `Journey/channelActivity.js`, `Journey/channelActivity.test.js`, and `WorkQueue.test.js`.

### Step boundaries (do not mistake placeholders for completed integrations)

| Capability | Status |
| --- | --- |
| Manual evidence, follow-up and outcome interface | Implemented here |
| Existing permitted customer signal preview | Implemented: current viewer-scoped excerpts and explicit purchase applicability |
| Continuous channel ingestion, attribution and refresh | Implemented: minute incremental scan, hourly reconciliation, private refresh hints and polling |
| Secure document/screening observations | Existing status only; receipt or AI analysis does not confirm financial readiness |
| Handoff acceptance, specialist clocks, advisor decision checklist, booking bridge | Step 7 |
| AI extraction proposals, quote playback and draft generation | Step 8 |
| Qualified-consultation target/KPIs, week/month reporting, full EOD checks and scheduled operations | Step 9 |
| Staff timed trial and integrated acceptance | Pending Step 10; Step 6 rollout measurements live in its deployment report |

“Messages to check” counts distinct accessible customers with an unresolved, matched source request. Missing owner/dated action remains the higher-priority No Action tier. A staff resolution follows an unchanged request across delivery metadata updates; an edited question reopens it. The qualified-consultation KPI remains absent until its verified metric is implemented. Basic Day plan is usable now; specialist/calendar/KPI panels arrive in their assigned steps. Preview is an explicit operation, not continuous recomputation on every keystroke.

## Reference usage

`WorkspaceController::store()` explicitly maps the named `journey_workflow` request into `StaffWorkflowRepository::execute()`. `JourneyEditor.vue` and `workflowForm.js` demonstrate preserving request identity and expected versions, followed by Inertia redirect/readback. Use this boundary rather than writing models from a controller or browser.

## Related files

- Backend: `app/Http/Controllers/Manage/RevenueJourney/WorkspaceController.php`; `DraftRequest.php`, `WorkspaceQueryRequest.php`, existing `WorkflowRequest.php` and `FinanceRequest.php` under `app/Http/Requests/Manage/RevenueJourney/`.
- Projection: `src/RevenueJourney/Services/JourneyWorkspace.php`, `WorkspacePresenter.php`.
- Preview: `src/RevenueJourney/Repositories/OutcomePreviewRepository.php`.
- Existing write authority: [workflow contract](/docs/modules_handbook/shared/revenue-journey/workflow-contract.md).
- Frontend: `resources/js/Pages/Manage/Ai/Copilot/WorkQueue.vue` and `Journey/{JourneyDrawer,JourneyEditor,DimensionDialog,ReadinessStrip,FactEditor,PrimaryFields,SupportRecords,FormFeedback}.vue`; `Journey/workflowForm.js`.
- Integration: `routes/web.php`, `HandleInertiaRequests.php`, `RevenueIntelligenceController.php`, `SectionTabs.vue`, `JourneyFeatureFlags.php`. Shared `Drawer.vue` now registers Escape on an already-open mount and restores its scroll lock after an inner dialog closes.
- Tests: `tests/Feature/RevenueJourney/WorkspaceTest.php`; `Journey/workflowForm.test.js`; existing Step 2–4, legacy Copilot, shared Modal and suite tests.
- Step 6 adds three additive migrations, a source-sync command/schedule and a signed SendGrid event receiver. It makes no external AI calls and sends no customer messages.
