# Customer Journey — foundation, rules and staff workspace (Steps 2–8)

## What it does

Stores a customer intake and separate purchase opportunities without changing existing sales, booking, commission or channel records. Stores evidence provenance, review history, financial references and future workflow records. The feature defaults off. Step 2 adds no route, replacement UI, scheduled job, provider call, message sender or readiness calculation.

Step 3 adds a side-effect-free readiness, funding, stage and queue rules engine. Step 4 connects this engine to transactional staff-command writers. Step 5 adds the permission-gated Inertia staff workspace. Step 6 connects stored channel data, creates eligible customer intakes and refreshes their current evidence. AI proposals and customer sends remain separately gated later stages. See [the staff workspace contract](/docs/modules_handbook/shared/revenue-journey/workspace-contract.md) for the interface, rollout controls and manual-pilot boundaries. See [the rules contract](rules-contract.md) for the complete internal input/output and integration obligations.

An intake is unique per customer. A purchase opportunity is a distinct intended purchase, can start before a property is selected, and is not unique by customer/project. More than one opportunity may link to the same legacy Engagement; real bookings remain in the existing Booking model.

## How it works

### Authority and storage map

All table names below start with `revenue_`. Schema definitions are additive; there are no destructive changes to legacy tables. References use indexed integer IDs internally and public UUIDs at command boundaries. V1 tenant placeholders are server-owned nulls; these columns do not grant partner tenancy.

| Tables | Authority and mutability |
| --- | --- |
| `intakes`, `opportunities` | Context identity, assignment/version and lifecycle projections. Repository creation produces an inactive, unassigned draft. |
| `targets`, `financing_routes` | Immutable versions of property/offer and financing terms. Local sales projects and federated catalogue projects remain different identities. Catalogue UUIDs are truth; numeric catalogue IDs are caches. |
| `sales_links`, `booking_links` | Links to canonical existing transactions; neither creates a transaction nor changes commission ownership. |
| `source_events`, `source_matches` | Revision/occurrence identity plus ingestion and attribution projections. Future mappers derive event keys from the correct source grain and retain case-sensitive provider identifiers. |
| `evidence` | Immutable typed assertions, original manual/source quote, origin/authority, timestamps, revision, correction lineage and deduplication identity. Recording does not confirm the assertion. |
| `evidence_reviews`, `evidence_links` | Immutable review decisions and explicit context relevance. Step 4 exposes internal staff review commands with immutable correction and reconfirmation decisions. |
| `effective_facts` | Rebuildable accepted-fact heads with dependency versions, authority and invalidation/conflict state. Step 4 rebuilds these synchronously in the same transaction as staff commands. |
| `commitments`, `handoffs` | Staff operational state. At most one open primary commitment and one pending/accepted ownership offer per context are database-enforced; Step 4 requires an owner and valid dated primary on activation. Host/specialist offers remain separate from ownership. |
| `events`, `command_receipts`, `outbox` | Immutable audit events, synchronous command retry receipts and dormant integration delivery records. No outbox consumer is installed. |
| `funding_pools`, `funding_tranches`, `cash_reserves`, `cash_allocations` | Stable funds identities and immutable revisions. Step 4 validates staff reservations against the Step 3 dated cash-conservation rules; storage constraints alone do not establish Cash Ready. |
| `contact_policies` | Customer-wide contact restriction projection. Existing WhatsApp category consent and contact blocking cannot represent this scope. Step 4 guards outbound follow-up creation; sender integration is a later release requirement. |
| `queue_snapshots`, `backfill_batches` | Rebuildable output/import metadata. Batch summaries must contain aggregate counts/versions only; never customer samples or quotes. |

There are **24 new tables**, in five migrations. Dimension values are 0–6 in the versioned registry; operational enums use model constants and unsigned integer storage.

### Write boundary

`DraftContextRepository` accepts only nested `journey_intake` / `purchase_opportunity` payloads. It resolves UUIDs, rechecks customer access and holds the customer lock before creating a context. It cannot accept client-supplied actor, tenant, owner, primary commitment or activation values. Creating a draft does not assign sales ownership.

`ManualEvidenceRepository` records pending manual customer statements for `need.purpose`, `need.budget`, `need.timing`, `need.must`, `need.concerns`, `loan.need` and `cash.source`. Intake is additionally limited to Need/Relationship. Step 4 adds permission-checked manual review and reference-resolving commands; externally measured fields still require their channel/operational adapters. No external channel, financial calculation or party identity can be invented through this scalar-note path. Corrections reference assertions from the same context/field; they do not move an effective head.

All command writes, receipt, audit event and dormant outbox entry commit together. The customer lock precedes the context lock. `expected_version` rejects a stale note with 409. Actor, source origin and authority come from the authenticated command. Canonical UUID casing and object-key ordering make equivalent retries identical. Input datetimes have explicit offsets and are normalized for hashing; stored DATETIME values follow the existing application timezone, preserving the original instant. Receipts retain only a payload hash and safe result UUID, and access is checked again before replay. Failed synchronous transactions leave no partially claimed command. Async lease recovery is not implemented or used.

Lead/intake merge preserves original receipt keys/results. A retargeted old request receives an explicit conflict rather than silently creating another purchase. An intentional new action requires a new request ID.

The repositories remain the write authority. Step 5 explicitly maps named Diver Form Request fields into these commands through the staff workspace controller; it returns viewer-scoped presentation arrays rather than whole models.

### Fields and permissions

`config/readiness.php` defines 31 typed fields, authority requirements and candidate capability bundles. `FieldRegistry` validates shapes, including exact money values, strict booleans, nested key allowlists, date precision and catalogue UUID identity. It does not resolve references, calculate readiness, or validate that a quote is factually correct.

`JourneyPolicy` intersects active staff access, the existing `LeadVisibility` rules, non-staff customer identity, null tenant scope, operation capability and context owner/coordinator. The scoped manager capability does not widen customer visibility. Customer facts, advisor assessments and financing reviews have separate capabilities. A generic human command cannot write system evidence or select a property target. Reviews of matched staff-recorded sources are supported. External customer quotes require an eligible current source, purchase match, customer speaker and an exact quote contained in the source. AI proposals remain unavailable. Read-only recovery remains possible when writes are disabled.

New permission names are catalogued but are not automatically granted to existing admin, sales or group roles. Super-admin access remains subject to feature and authority gates. Candidate role mappings are guidance for an explicit future pilot, not grants.

### Release controls

All five settings default off; only the exact value `'1'` enables a setting. The resolver is request/job scoped.

- `Setting::JOURNEY_WRITES`: shared mutation gate.
- `Setting::JOURNEY_QUEUE`: staff workspace visibility. `workspace()` remains enabled for read-only recovery when writes are off; `queue()` still requires writes for later processing.
- `Setting::JOURNEY_INGESTION`: existing-channel import; additionally requires queue and writes.
- `Setting::JOURNEY_EXTRACTION`: future extraction, additionally requires writes.
- `Setting::JOURNEY_AUTOMATION`: future automation, additionally requires writes.

Step 2 neither seeds enabled values nor registers consumers. Enabling a flag alone does not implement the later workflow. Existing Copilot and channel screens continue to use their current implementation.

### Merge and privacy compatibility

`LeadRepository` calls `JourneyIdentity` inside its existing merge/purge transactions. Distinct opportunities and funding pools retain their identity. Compatible intakes combine; competing open commitments, owners/coordinators or pending ownership offers produce a safe 409 and roll back the entire merge. Original deadlines remain unchanged. Divergent effective facts become a visible conflict rather than arbitrarily choosing a winner. Engagement collision handling repoints sales links before the legacy engagement is removed. The strictest contact restriction survives.

Identity merge and privacy purge are explicit raw-database exceptions to immutable-model guards. A purge traverses context, evidence, source, funding and transaction-link descendants. If a source is shared with a surviving context, its opaque identity remains as a redacted shell, copied assertions/review notes are redacted and invalidated, and affected snapshots are removed. Step 6 adds hashed provider-addressable suppression records and rechecks source authority at read/import time. Source types without stable provider identity retain their local identity suppression. Provider-side retention remains the channel importer’s responsibility. This compatibility layer is not a complete provider retention implementation.

All new customer/staff/actor references are classified in `IdentityChildMap`. The current application baseline has **52 existing unclassified references outside this module**. The isolated before/after census confirms the same 52 and zero new gaps. That existing global identity/purge backlog must be resolved before broad rollout; it is not hidden by weakening the existing test.

### Read-only cohort preview

`CohortPreview::build()` accepts an explicit list of 1–50 customer UUIDs and a cutoff. It filters by existing customer access and journey capabilities; staff records are excluded. It returns structural sales/booking links, current draft identities and counts of open, undated or unassigned legacy tasks. It creates no rows and does not invent a score, deadline, assignment, or activation.

The fingerprint is deterministic for the same visible structural report. The cutoff limits creation eligibility; the report describes current rows, not a historical reconstruction or immutable/full-source backfill manifest. Channel attribution, an approved pilot cohort and import execution remain later work.

## Reference usage

Use the repositories from authenticated application services; do not call model `create()` directly from future controllers. `FoundationInput` and `FieldRegistry` are reusable internal validation boundaries. Models enforce local mass-assignment allowlists because the existing Diver base globally unguards models. Immutable record builders reject update/delete/upsert/quiet mutation paths; approved merge/privacy code is the documented exception.

Do not calculate confirmation from an assertion's presence, reuse old Zoom scores as seven-dimension readiness, or render financing data as Ready merely because the new tables contain values. Manual party identities use immutable event roots; matched staff-recorded statements/outcomes provide session references. External channel and secure document references require their later adapters. Shared cash and stage/priority proposals are calculated in Step 3. Step 4 provides the transactional operational commands; later handoff/booking integration retains its own boundaries.

See [the staff workflow contract](workflow-contract.md) for command payloads, clock semantics, shared cash locking, manual-source limits and Step 5 integration.

## Related files

- `docs/modules_handbook/shared/revenue-journey/workflow-contract.md` (Step 4 command and test inventory)

- `docs/modules_handbook/shared/revenue-journey/rules-contract.md` (Step 3 contract and complete rules/test inventory)

- `src/RevenueJourney/BackfillBatch.php`
- `src/RevenueJourney/BookingLink.php`
- `src/RevenueJourney/CashAllocation.php`
- `src/RevenueJourney/CashReserve.php`
- `src/RevenueJourney/CommandReceipt.php`
- `src/RevenueJourney/Concerns/ImmutableBuilder.php`
- `src/RevenueJourney/Concerns/ImmutableRecord.php`
- `src/RevenueJourney/ContactPolicy.php`
- `src/RevenueJourney/EffectiveFact.php`
- `src/RevenueJourney/EvidenceAssertion.php`
- `src/RevenueJourney/EvidenceLink.php`
- `src/RevenueJourney/EvidenceReview.php`
- `src/RevenueJourney/FinancingRoute.php`
- `src/RevenueJourney/FoundationModel.php`
- `src/RevenueJourney/FundingPool.php`
- `src/RevenueJourney/FundingTranche.php`
- `src/RevenueJourney/JourneyCommitment.php`
- `src/RevenueJourney/JourneyEvent.php`
- `src/RevenueJourney/JourneyHandoff.php`
- `src/RevenueJourney/JourneyIntake.php`
- `src/RevenueJourney/OpportunityTarget.php`
- `src/RevenueJourney/OutboxEvent.php`
- `src/RevenueJourney/Policies/JourneyPolicy.php`
- `src/RevenueJourney/PurchaseOpportunity.php`
- `src/RevenueJourney/QueueSnapshot.php`
- `src/RevenueJourney/Repositories/DraftContextRepository.php`
- `src/RevenueJourney/Repositories/FoundationJournalRepository.php`
- `src/RevenueJourney/Repositories/ManualEvidenceRepository.php`
- `src/RevenueJourney/SalesLink.php`
- `src/RevenueJourney/Services/CohortPreview.php`
- `src/RevenueJourney/SourceEvent.php`
- `src/RevenueJourney/SourceMatch.php`
- `src/RevenueJourney/Support/FieldRegistry.php`
- `src/RevenueJourney/Support/FoundationInput.php`
- `src/RevenueJourney/Support/JourneyFeatureFlags.php`
- `src/RevenueJourney/Support/JourneyFeatureResolver.php`
- `src/RevenueJourney/Support/JourneyIdentity.php`
- `src/RevenueJourney/Support/JourneyIdentityConflict.php`
- `tests/Feature/RevenueJourney/CohortPreviewTest.php`
- `tests/Feature/RevenueJourney/FoundationCoverageTest.php`
- `tests/Feature/RevenueJourney/FoundationRepositoryTest.php`
- `tests/Feature/RevenueJourney/FoundationSchemaTest.php`
- `tests/Feature/RevenueJourney/JourneyFeatureTest.php`
- `tests/Feature/RevenueJourney/JourneyIdentityTest.php`
- `tests/Unit/RevenueJourney/RegistryPolicyTest.php`
- `config/readiness.php`
- `src/Auth/Permission.php`
- `src/Setting/Setting.php`
- `database/seeds/RolesSeeder.php`
- `app/Providers/AppServiceProvider.php`
- `src/Lead/Support/IdentityChildMap.php`
- `src/Lead/Repositories/LeadRepository.php`
- `app/Http/Controllers/Manage/People/MergeRequestsController.php`
- `database/migrations/2026_09_16_120001_create_revenue_journey_foundation_1.php`
- `database/migrations/2026_09_16_120002_create_revenue_journey_foundation_2.php`
- `database/migrations/2026_09_16_120003_create_revenue_journey_foundation_3.php`
- `database/migrations/2026_09_16_120004_create_revenue_journey_foundation_4.php`
- `database/migrations/2026_09_16_120005_create_revenue_journey_foundation_5.php`

Step 5 adds the routes, frontend and presenters listed in [workspace-contract.md](/docs/modules_handbook/shared/revenue-journey/workspace-contract.md). No scheduled jobs, provider adapters or migrations are added in Step 5.

## Step 7 — handoffs, decision review and booking

Staff can request and accept separate consultation-host, customer-owner and specialist responsibilities. The queue shows the responsible teammate, explicit consultation attendance, missing outcomes, decision exceptions and the live Sales booking status. These actions reuse the existing schema.

- [Handoff workflow](handoff-contract.md): custody, acceptance, consultation attestation, deadlines and role checks.
- [Decision and booking workflow](booking-contract.md): current checklist, expiring exceptions, private receipts and canonical Sales integration.
- UI: `TeamResponsibility.vue`, `DecisionPanel.vue` and their dialogs in `resources/js/Pages/Manage/Ai/Copilot/Journey`.
- HTTP boundary: `app/Http/Controllers/Manage/RevenueJourney/OperationsController.php`.

Shared repositories are called by the customer workspace and snapshot refresh. Locked commands always reload evidence and permissions; request-local read batches never authorize writes. No schema migration, new AI call or external customer message is introduced by this step.


## Step 8 — AI preparation and the complete readiness view

The customer drawer shows all seven readiness areas, including unknown purchase-specific areas on an initial enquiry. A manager can start the intended purchase before a property is shortlisted. Current Need and Relationship evidence can be linked to that customer's first purchase; later purchases need their own relevance checks.

Managers can request source analysis, identify recording customer speakers, review quoted proposals, resolve conflicts and prepare an editable call brief or WhatsApp/email draft. Accepted evidence changes readiness through the existing deterministic rules. Suggestions alone never do. The existing AI client and configured company provider handle extraction and drafts; customer credits and channel senders are not called.

- [AI validation and evaluation](ai-validation-contract.md): quotation, authority, typed fields, redaction and the real-recording release gate.
- [AI preparation operations](ai-preparation-contract.md): durable requests, review flows, shadow controls and recovery.

No schema migration is required. Manager preview and general staff rollout are separate controls. A synthetic test pass is not a passed real-recording evaluation.

## Daily operations (Step 9)

The Work queue now includes **Day plan** and a permission-gated **Team** view. Both use the same current, viewer-authorized customer/source projection as the queue. End-of-day checks open the affected customer; team deadline filters and owner drilldowns preserve the suite and existing outcome workflow.

See [daily-operations.md](daily-operations.md) for deadlines, KPI definitions, dispatch controls, retention boundaries and recovery.

## Engagement priority and queue performance

See [customer engagement](customer-engagement.md) for paid membership, measured webinar attendance, previous lost buyers, evidence boundaries and eligibility. See [queue performance](queue-performance.md) for measured timings, snapshot freshness and remaining performance targets. Queue navigation loads only the selected customer. Background status checks do not reload the customer table; staff choose Update view when new data is available.

Existing active CRM sales staff can receive basic journey access through `journey:staff-access` (preview) and `journey:staff-access --apply --user=<uuid>` (apply to reviewed staff). This grants view, workflow management and customer-fact recording only. Existing lead/channel scope, advisor and financing permissions are unchanged.
