Lifecycle stages
A relationship sits at one of six points — prospect, qualified, opportunity, client, onboarding, active — plus *not proceeding*, which is the absence of a step rather than a further one. journey.advance moves a relationship forward, records who moved it and why, and refuses to make somebody a client on its own because that is a commercial commitment.
The six, and the seventh that is not one#
prospect → qualified → opportunity → client → onboarding → activeThat is the shortest sequence which still separates the things a business treats differently: a name on a list, somebody who replied, somebody who wants a quote, somebody who bought, somebody being set up, and a customer who occasionally needs something. Connect is not trying to be a CRM here — there are no custom pipelines, no probability weightings and no forecast. It needs to know which of the six applies so it can behave correctly.
*Not proceeding* (closed) sits deliberately outside the sequence. Giving it a position would let a move be refused for a customer who came back a year later; ranked as *before everything*, any real signal moves them out of it again. The same is true of lost, not_interested and the other terminal words — a relationship that was closed and writes to you again is starting over, not resuming at the end.
What a move actually changes#
| What changes | Where | Why it is there |
|---|---|---|
| The current position | people.stage | One answer to where the relationship stands |
| A transition event | relationship_stage_events | Who moved it, from what, to what, with a reason — a column alone cannot settle a disputed invoice |
| A timeline entry | Assembled by timeline.py | The move is an event, readable in the order it happened |
| An audit record | relationship.stage_changed in audit.py | A commercial fact — 'they became a client on the 3rd' — with the actor recorded |
The contact record is kept in step at the same moment, because the pipeline screen and the prospect list still read contacts.stage. Two answers to the same question is the drift this module exists to end.
Three rules that hold everywhere#
- History is never lost
- The position lives on the person and every transition is also written as its own event. You can always answer *when did they become a client, and on whose say-so?*
- Every transition is idempotent
- The same evidence arrives more than once — a reprocessed message, a retried request, a redelivered webhook — and must move the relationship once.
referenceis the key; a repeat returns the original event rather than writing a second. - Becoming a client is not Connect's decision alone
clientis on the needs-a-human list. Connect may propose it; the workspace's own approval rules put it in front of a person, the same rules every outbound action uses.
A forward move that Connect can make on its own still records that it made it. The audit trail carries refusals as well as actions, because a refusal is a decision — the decision log is where that is read.
Moving backwards, and other refusals#
- Backwards. A move to a position at or behind the current one is refused unless it is asked for explicitly. Progress is not undone by a stray signal.
- A word Connect does not know. A destination outside the six is refused outright with
unknown_stagerather than written as a new position. Reads are forgiving; writes are not — see normalising a stage. - A commitment awaiting a person. Where the destination is
clientand Connect is the actor, the move is held and an approval is raised. The relationship stays where it was until somebody says yes.
Questions#
Can I add my own stages?
No. The six are fixed, deliberately — custom pipelines, weightings and forecasting are what a CRM is for, and Connect's job is to know which of the six applies so it can act correctly. Sales has its own stage rules for an opportunity, which is a different record.
Why was a move refused as unknown?
Because the destination was not one of the six. advance refuses rather than inventing a position, which is what keeps the vocabulary from growing a seventh entry every time an import uses a different word.
Who can move a relationship to client?
A person. Connect can propose it and the workspace's approval rules decide when it is put in front of somebody; the transition itself waits for a human yes, and the approval id is recorded on the event alongside the actor and the reason.