# CONTRACT.md — Ferry

What this product does. Every clause has a stable ID and at least one tagged test; `pnpm verify` reports each as satisfied, overridden, or broken. Security, privacy, and operational guarantees are in `BASELINE.md`; a clause here may supersede one of them by naming its `BASE-` ID (none does). Capacity and rate limits are in `seed.json → limits`. Times are p95 at those limits. Clauses marked *Policy* have a default that buyers may replace through the named extension point.

## Scope

An intervention-first sales pipeline for one business. A small group of equal operators runs one configurable deal pipeline: deals, the people and organizations behind them, activities, customer commitments, and human-approved follow-up email. Ferry turns evidence that a deal is slipping into an explainable Intervention Brief — what changed, why it matters, the next credible action, and the buyer commitment that would make progress real. It is a selling-execution system, not a contact database, a marketing tool, or a reporting tool.

## Public landing

- **HOME-001 — Owner identity plate.** `/` presents the configured organization and owner name, one configured landing line, and a sign-in link. It may show the configured static Ferry credit. It contains no marketing claims, product metrics, testimonials, or telemetry.

## Operators

- **TEAM-001 — Equal operators.** Every operator can see and act on every business record. Ownership and assignment route work; they never restrict access.
- **TEAM-002 — Management.** Operators are invited and removed in settings. The operator configured at setup can be removed only by themselves. *Policy: `operator.management.v1`; default: any operator may invite or remove.*
- **TEAM-003 — Business settings.** Settings hold one IANA business time zone and one default currency (PIPE-010). Every due time, quiet-hours window, day boundary, and Insights date range is evaluated in that time zone. Operators do not have individual time zones or individual currencies.

## Pipeline and deals

- **PIPE-001 — One configurable pipeline.** The business has one ordered set of open stages. Operators can add, rename, reorder, and retire stages; retiring a non-empty stage requires moving its open deals first. *Policy: `pipeline.stages.v1`; default: Discovery, Qualified, Proposal, and Negotiation.*
- **PIPE-002 — Deal identity.** Creating a deal requires a title and at least one linked person or organization. Each open deal has exactly one stage; value, currency, expected close date, owner, and primary contact may be recorded, and any missing value is shown as `Not set` rather than invented.
- **PIPE-003 — Decision board.** The active board shows every open deal in its stage. Each stage shows a derived deal count and its total value under PIPE-010; each card shows title, account or contact, value, health, and an explicit next-action state of overdue, no next action, due today, or a future due time.
- **PIPE-004 — Work ordering.** The default board order is overdue action, no next action, due today, then future action; ties use due time, expected close date, then deal ID. Operators can filter by owner, health, stage, and activity state without changing the records included in other operators' views.
- **PIPE-005 — Stage movement.** An operator can move an open deal to any configured open stage. A stage may be configured with named exit conditions; each condition is satisfied by one recorded thing — an activity of a named type marked done, a required relationship role filled (REL-002), an open or completed customer commitment, or a manually attested fact (EVID-001). When a target stage's exit conditions are not all satisfied, Ferry names each unsatisfied condition and offers keep stage, record the missing evidence, or move anyway with a required reason. An override records the reason and the unsatisfied conditions in Deal Story and never marks a condition satisfied. *Policy: `deal.stage-transition.v1`; default: no stage has exit conditions, so every move proceeds without a prompt. A replacement may add, remove, or change conditions; it cannot remove the override reason requirement or suppress the override record.*
- **PIPE-006 — Won and lost.** Marking a deal won or lost records the outcome, the actor, and the outcome time, and closes the deal (PIPE-011). Lost requires a reason. Open activities and open commitments on the deal keep their state and due times but stop producing reminders (ACT-004) and briefs (INT-001) while the deal is closed. *Policy: `deal.close-reason.v1`; default: any non-empty free-text reason for lost, none required for won.*
- **PIPE-007 — Historical truth.** Changes to stage, value, currency, expected close date, owner, relationships, and outcome append an actor-attributed before/after event to Deal Story. Editing current fields never rewrites or removes those historical events.
- **PIPE-008 — Concurrent edits.** A mutation based on a stale record version returns a conflict with the current values and changes nothing. The operator can review and deliberately reapply their edit; Ferry never silently overwrites newer work.
- **PIPE-009 — Distinct time signals.** Every open deal records and displays four separate times, each with its own date and never merged into one number: the last recorded buyer interaction, the last recorded customer commitment, the due time of the next planned activity, and the time the deal entered its current stage. Editing a deal's fields, opening the deal, or sending an operator message changes none of them; only the underlying buyer, commitment, activity, or stage record does. Stage entry time changes only on a stage change and survives closing and reopening.
- **PIPE-010 — Currency.** A deal's value is recorded in the business default currency (TEAM-003) unless an operator selects another ISO 4217 currency for that deal; changing a deal's currency never converts its value. Ferry retrieves no exchange rates. An operator may record a manual rate for a currency against the default. A total or ranking that spans currencies uses only operator-recorded rates, states that it is converted, and names each rate and the date it was recorded; currencies with no recorded rate are excluded from that figure and listed with their own separate totals.
- **PIPE-011 — Closed deals stay reachable.** Won and lost are outcomes, not stages: neither appears as a board column. A won or lost deal keeps its complete Deal Story, activities, commitments, evidence, and messages. It is excluded from the board, from Focus, and from every open-deal figure in Insights; it is included in global search (SEARCH-001), CSV export (EXPORT-001), and the portable archive. Ferry assigns no health state to a closed deal.
- **PIPE-012 — Reopening.** Reopening a closed deal requires selecting an open stage and appends a reopening event; the prior outcome, its reason, and its time remain in Deal Story. Activities and commitments suspended by PIPE-006 resume producing reminders and briefs with their original due times.

## Relationships

- **REL-001 — People and organizations.** People and organizations are distinct records linked bidirectionally to their deals, activities, commitments, evidence, and messages. A person may belong to one organization and may hold one or more named roles on a deal.
- **REL-002 — Relationship coverage.** Deal Story shows, for each person linked to the deal, their role, the operator who owns that relationship, and the date of the most recent recorded interaction with them. Coverage for the deal is derived as complete when every role required at the deal's current stage is filled by a linked person, gap when at least one required role is unfilled, and critical gap when a role that became required at a stage before the deal's current one is still unfilled; every unfilled required role is named. *Policy: `relationship.required-roles.v1`; default: economic buyer and champion for every open deal, plus procurement and security reviewer from Proposal onward.*
- **REL-003 — Duplicate safety.** A trimmed, case-insensitive non-empty email address identifies at most one person, and a trimmed, case-insensitive name identifies at most one organization. A create or import collision surfaces the existing record and does not merge or overwrite it; similar person names, phone numbers, and similar organization names are warnings only.
- **REL-004 — Manual merge.** Before merging two people, or two organizations, Ferry previews every field conflict and every reparented deal, activity, commitment, evidence item, person, and message. The operator chooses the surviving values; the merge applies atomically and appends an audit event, or changes nothing on failure. Merging is never performed automatically.
- **REL-005 — Contact and organization lists.** Operators can list people and organizations and filter them by organization, deal role, and relationship owner. Each person row shows their role, relationship owner, the date of the most recent recorded interaction, and their linked deals. Lists are paginated and state the total number of matching records; a displayed count never disagrees with the set it summarises.

## Activities

- **ACT-001 — Activity lifecycle.** A call, meeting, task, email follow-up, or deadline has a subject, owner, due time evaluated in the business time zone (TEAM-003), and links to a deal plus optional people and organization. Its stored state is planned, done, or cancelled; overdue is derived when a planned activity's due time passes.
- **ACT-002 — Completion truth.** Completing an activity records the actual completion time once and preserves its original due time. Repeating the completion operation is a no-op. Completion prompts the operator to create a next activity or record why none exists, but never creates one or changes deal health by itself.
- **ACT-003 — Daily work.** Operators can list and filter activities by overdue, today, future, owner, type, and linked deal. Completing, rescheduling, cancelling, or reassigning an activity updates the relevant queue immediately while its prior state remains in Deal Story.
- **ACT-004 — Reminders and quiet hours.** In-app reminders appear when a planned activity becomes due and again when it becomes overdue. Email reminders are opt-in and are sent within 5 minutes of that transition, or within 5 minutes of the end of quiet hours if the transition falls inside them. A reminder is sent once per due-state transition and is cancelled when the activity is completed, cancelled, rescheduled, or reassigned. An operator-initiated send (MAIL-001) is never delayed by quiet hours. *Policy: `activity.reminder.v1`; default: in-app reminders on, email reminders off, quiet hours 19:00 to 08:00 in the business time zone.*

## Customer commitments

- **COMMIT-001 — Qualified commitment.** A customer commitment belongs to one deal and records a named customer person, a concrete promised outcome, a due time, the operator who recorded it, and either a linked source event or an explicit manual attestation. An internal task or an operator's own promise is not a customer commitment.
- **COMMIT-002 — Lifecycle.** A commitment is open, completed, or cancelled; overdue is derived when an open commitment's due time passes. Completing, revising, or cancelling one appends the actor, time, and prior values; revisions never rewrite the original promise.
- **COMMIT-003 — Canonical visibility.** The same commitment record appears in Focus, Deal Story, the daily work queue, and Insights. Updating it in any surface produces the same resulting state everywhere.
- **COMMIT-004 — Idempotent outcomes.** Repeating the same completion, cancellation, or revision request produces no duplicate event and no second health assessment.

## Evidence and Deal Story

- **EVID-001 — Facts.** A fact is an attributable, timestamped record of a customer or operator event. Every fact shown in an Intervention Brief links to its source event; manually recorded facts identify their recorder and are labelled manual.
- **EVID-002 — Inferences.** An inference is stored and displayed separately from facts, names the facts it relies on, and can be corrected or dismissed without altering those facts. Inferred content is never presented as customer confirmation.
- **STORY-001 — Canonical context.** Deal Story presents the current commercial fields, relationships, commitments, facts, inferences, activities, outbound messages, health assessments, and attributed history for one deal in chronological order.
- **STORY-002 — Record correction.** Correcting a current field, manual fact, or inference appends the correction and actor to Deal Story. Source events and previously issued health assessments remain visible as historical context.

## Health and Intervention Briefs

- **INT-001 — Focus is the default.** After sign-in, Focus opens as a projection over canonical deal records, not a separate task store. It shows one expanded Intervention Brief and no more than the next two ranked briefs; a healthy queue states when no intervention needs action and still shows today's commitments.
- **INT-002 — Explainable health.** Every open deal has exactly one current state — On track, Watching, Slipping, At risk, or Not actionable — and a rationale linked to the contributing records. The states are evaluated in this order, and every open deal matches one. Not actionable is set by an operator under INT-007. Otherwise, by default: Slipping means an open commitment or the next planned activity is overdue; At risk means Slipping with an expected close date inside 30 days and a credible intervention available (INT-011); On track means nothing is overdue, the deal has an open qualified commitment, and a planned next activity is due no later than that commitment; Watching is every remaining open deal — nothing is overdue, but a stage exit condition (PIPE-005) or required relationship role (REL-002) is unfilled, or the deal has no open qualified commitment, or it has no planned next activity. Record-update time alone never changes health. *Policy: `deal.health-assessment.v1`; default: the conditions above. A replacement may change those conditions and thresholds but may not add a state outside this list, may not leave an open deal without a state, and may not make an outbound message, a record edit, or elapsed time alone produce On track (INT-008).*
- **INT-003 — Explainable ranking.** Focus ranks active briefs by recoverable deal value (PIPE-010), evidence of broken buyer momentum, time sensitivity, and whether a credible intervention exists; it displays those four factors instead of a percentage score. Equal briefs sort by earliest commitment or activity due time, then deal ID. *Policy: `intervention.ranking.v1`; default: the four factors above, ordered so that a brief with a credible intervention always outranks one without.*
- **INT-004 — Brief anatomy.** An expanded brief shows impact, why now, the four time signals from PIPE-009, two to four cited facts or labelled inferences, one recommended action with its intended outcome and the source named under INT-011, controls, and the qualified buyer commitment that would demonstrate progress.
- **INT-005 — Unsafe recommendation.** Missing or conflicting evidence produces `Cannot recommend safely`, names the missing or conflicting inputs, and offers only record correction, evidence capture, or Deal Story navigation. Ferry never fabricates evidence, a recommendation, or a recipient to fill a gap.
- **INT-006 — Human-controlled action.** A prepared email, call, or task is editable. An operator may switch action, revise recipients or content, abandon it, or explicitly confirm it. Ferry never sends a message, creates an activity, or completes an activity without an explicit operator confirmation; no policy, extension, or setting enables autonomous sending or autonomous record changes.
- **INT-007 — Deferral and not actionable.** Deferring a brief requires a reason and a revisit time. Marking a deal Not actionable requires a reason and one next disposition — re-qualify, move the expected close date, nurture, reassign the deal to another operator, or close lost. Neither choice removes the record or its history.
- **INT-008 — Action is not progress.** Sending or scheduling a follow-up appends its action records but does not improve deal health. Only a commitment satisfying COMMIT-001, together with the next activity required by INT-002, may produce a new On track assessment.
- **INT-009 — Intervention deduplication.** At most one active brief exists for the same deal and causal evidence set. Re-evaluating or redelivering the same evidence updates that brief instead of creating another; materially different evidence may create a new brief and supersedes any now-stale recommendation.
- **INT-010 — Evaluation failure.** If health or intervention evaluation fails, canonical deals, activities, and commitments remain usable and no prepared action is dispatched. An affected brief shows its last successful evaluation time and a visible failure. When no evaluation has succeeded for 30 minutes, Focus shows a stale-evaluation warning with that time above the queue, whether or not any brief exists. A successful re-evaluation clears the warning without erasing the failure from operations history.
- **INT-011 — Recommended action source.** The recommended action in a brief is produced from the deal's own records, and the brief names the built-in playbook that produced it and the records it used. Playbooks are part of the recommendation policy, not an operator-editable object. *Policy: `intervention.recommendation.v1`; default: playbooks for a missed customer commitment, an unfilled required role, no recorded buyer interaction since the deal's last outbound message, and an expected close date that has passed or falls within 7 days with no planned next activity.*
- **INT-012 — No generated or opaque numbers.** Ferry calls no AI provider and declares none in `seed.json → externals`; no content is labelled or presented as AI-generated. No number shown to an operator is a score: every health state, ranking factor, coverage state, and Insights figure names the records or the formula it derives from.
- **INT-013 — When health is recalculated.** A deal's health is recalculated within 5 seconds of a change to its stage, value, expected close date, relationships, activities, commitments, or evidence, and for every open deal at least every 15 minutes so that a due time passing changes health with no record change. Each assessment records the time it ran and the records it used.

## Outbound follow-up

- **MAIL-001 — Confirmed dispatch.** Confirming `Send and schedule` transactionally creates one outbound message and one linked next activity before dispatch. If that transaction cannot commit, neither is created and no provider request is made. The provider request is made within 60 seconds of confirmation through `mail.sender.v1`; only the confirmed recipients and content leave the deployment.
- **MAIL-002 — Delivery states.** An outbound message is draft, queued, accepted, or failed. The UI says sent only after the provider accepts the message; a later rejection or bounce changes it to failed and shows the provider reason without changing deal health.
- **MAIL-003 — No duplicate outreach.** Double-clicks, client retries, queue redelivery, and provider callback replay cannot create or send a second message. Every attempt reuses the message's stable ID as its idempotency key.
- **MAIL-004 — Failure and retry.** Transient provider failures retry up to five times over one hour. A terminal failure remains visible on the message and its linked activity with a deliberate retry action; retry reuses the original message ID and never hides the failed attempt.

## Search and import

- **SEARCH-001 — Global retrieval.** Global search matches deal titles, organization and person names, person emails, activity subjects, commitment text, and message subjects, across open and closed deals, and returns within 500ms. Each result identifies its type and whether its deal is closed, and opens the canonical record.
- **IMPORT-001 — Preview before mutation.** CSV import accepts one entity type per file — deals, people, organizations, or activities. It maps columns and validates every row before confirmation, showing creates, ID-based updates, collisions under REL-003, warnings, and rejected rows with their original row numbers and specific reasons. A file at the row limit in `seed.json` is previewed within 60 seconds.
- **IMPORT-002 — Safe execution.** A confirmed import processes each row transactionally and reports accepted and rejected counts without silent skips. Each attempt has a durable ID and records the file's checksum; the file itself is not retained. Retrying an attempt requires re-uploading a file with the same checksum and continues that attempt rather than producing a second result; a different checksum starts a new attempt.
- **IMPORT-003 — Deterministic updates.** An Ferry ID updates that record. Without an ID, a deal row creates a deal and title similarity is only a warning; person and organization collisions follow REL-003. Imported values never overwrite an existing person or organization automatically.

## Insights

- **INSIGHT-001 — Declared metrics.** Insights shows open commitments due, active interventions, and recoverable revenue. Recoverable revenue is the recorded value of open Slipping or At risk deals that have an active Intervention Brief, reported under PIPE-010; the UI exposes the formula, filters, date range, and records included.
- **INSIGHT-002 — Drill-through.** Every commitment, intervention, health, and revenue row opens the corresponding Deal Story or Intervention Brief with the same filters preserved on return.
- **INSIGHT-003 — Figures match their records.** A metric shows a change against an earlier period only when Ferry holds the stored records for that period; the comparison names its window and opens the records behind both values. A metric with no stored prior period shows its current value and no trend. Every headline figure agrees with the rows the same view lists beneath it.

## Records and data

- **FIELD-001 — Custom fields.** Operators define custom fields on deals, people, organizations, and activities in settings, each with a name and type. Values appear on the record and in Deal Story, and travel through CSV export and import (EXPORT-001, IMPORT-003). Removing a field definition hides it from entry forms and keeps every recorded value in Deal Story and in exports until an operator deletes it.
- **EXPORT-001 — Export contents.** In addition to the complete portable archive required by BASE-DATA-001, operator CSV exports include stable Ferry IDs, linked-record IDs, custom fields, stage and outcome history, original activity due times, actual completion times, and commitment states, so that an edited export can be reimported as deterministic updates.

## Out of scope

Declared in `seed.json` as non-goals and not tested: multiple workspaces or pipelines; role, territory, or record-visibility hierarchies; a separate lead object; product catalogs, quotes, invoices, and contracts; marketing campaigns and sequences; autonomous outbound actions; AI scoring or general AI chat; retrieved exchange rates or currency conversion beyond operator-recorded rates; per-operator time zones and locales; inbound mailbox, Gmail, Outlook, or calendar synchronization; recurring activities; public forms, widgets, and booking pages; file attachments; SMS and calling; arbitrary report builders, custom dashboards, goals, and team forecasting; public APIs and webhooks; native mobile apps; and post-sale project or customer-success management.
