Handshake Flow
Overview
The Handshake is the explicit consent event by which a Knight accepts a proposed Shrubbery from a Lead. It is the moment a commitment becomes load-bearing — until it happens, no Say-Do Ratio counts and no deadline is enforced. The flow ends in one of two terminal states: Active (Knight accepted) or Refused (Knight declined).
Actors
- Lead — proposes the Shrubbery (always the
assigneron the row). - Knight — receives the proposal, reviews it in the Accord, and accepts or refuses.
- Squire (optional) — only present if the Shrubbery originated from Smart Paste; it drafted the row but does not participate in the Handshake itself.
Flow
- Lead sends a Shrubbery to the Knight —
Draft→Pending_Handshake. A row appears in the Knight's Accord. An audit event is appended withactor_kind = user. - Knight opens the Accord. The Shrubbery surfaces with its objective, deadline, source excerpt, and an Accept/Refuse pair.
- Knight accepts —
Pending_Handshake→Active. The Shrubbery now contributes to the team CFS. Audit event:actor_kind = user, typehandshake_accepted.- Or Knight refuses —
Pending_Handshake→Refused. The row counts toward Ghost Rate. Audit event type:handshake_refused.
- Or Knight refuses —
- Lead sees the result on the Garden card colour and the Knight's Dossier counter.
Edge cases
- Stale Pending_Handshake — Inngest fires a Nudge after the configured stale threshold. The Shrubbery stays in
Pending_Handshake; the Nudge does not auto-refuse. - Externally-sourced Shrubberies — when the Inbox API ingests a payload, the row starts in
Pending_Handshakedirectly (notDraft), because the Lead's intent is already declared by the API Token mint. The Handshake step is otherwise identical. - Renegotiation — a substantive edit (deadline / wording) by the Lead on an
ActiveShrubbery emits aRenegotiatedevent and reverts state toPending_Handshake, re-arming the flow. Non-substantive edits stayActiveand only emit anEditedevent. - Lead self-acceptance — disallowed. The Knight is always a different user from the Lead on a single Shrubbery row.
- Concurrent accept + refuse — the database enforces a single terminal transition. Whichever request wins commits first; the loser receives a 409 and the UI refreshes.
Related concepts
Last updated: 17 May 2026