# HANDOVER.md — Beacon conversation desk, bounded Stage 2 checkpoint

Read [first-release acceptance](ACCEPTANCE.md) for purpose, unmet release obligations, preserved requirements and the evidence decision rule.

## Current truth

This worktree carries the Beacon conversation-desk contract from the accepted
research synthesis at 193279be0423cc113870ccc57bb7ee481593ff9b and a bounded
Stage 2 implementation checkpoint. It is not the declared 1.0 product.

The executable registry at `tests/contract-status.mjs` records seven product clauses
full: HOME-001, OPS-003, WIDGET-001, CASE-011, EVID-007, EVID-008, and
RCPT-003. The remaining 169 product clauses are TODO. CASE-011 has both its real
Worker all-column source immutability proof in `tests/conversation-workflow.test.mjs`
and its Start browser follow-up journey in
`tests/e2e/case-follow-up.capture.spec.ts`; the earlier Stage 2 16/16 browser
execution is retained at
`/Users/zemaj/.orchestrator/evidence/crisp/conversation-desk-contract-and-1-0-built/stage2-root-start-full-e2e-1.log`.
BASE-ACCESS-002 is recorded full
from same-run real D1 session-expiry/sign-out evidence plus the custom-entry
API, document, generated-RPC, and Origin matrix. BASE-DATA-002 is recorded
full from a real scheduled Worker run that snapshots an ordinary twelve-row
D1 customer/case/approval/action/receipt graph and R2 bytes before and after
Cron, while exercising only the exact elapsed rate-window cleanup boundary.
The baseline has 19 ordinary TODO clauses and BASE-DATA-003 is superseded by
TODO DATA-002. The implemented
slices below are real local Worker/D1/R2 work and do not reclassify the
remaining contract declaration.

The custom `src/server.ts` Worker dispatches emitted assets, TanStack Start
documents and generated server functions, while the composed Hono Worker keeps
native HTTP, Queue, and scheduled handlers. Start documents are live: current
pages are not prerendered. Public documents and generated server functions are
rate-admitted before Start dispatch; `/app` documents independently verify a
native operator session. The emitted artifact has local, post-build Worker
coverage, and the bounded Support pilot below now adds scoped hosted acceptance.
Neither establishes whole-product hosted acceptance.

Locally exercised platform work includes native magic-link sessions and grants;
manual conversation/case records and guarded mutations; immutable knowledge,
report, evidence, decision, approval, action, receipt, reviewed-send, and
operator-notification storage and authenticated delivery-state UI slices; typed
D1/R2/Queue/Resend/OpenAI/operations ports; deterministic setup and
initial-operator bootstrap; and portable D1/R2 export with an empty-target
restore. Health checks D1, R2, and the declared Queue. The one-minute scheduler
handles durable action, AI, attachment, erasure, receipt, and notification
recovery. These notification slices do not complete NOTIFY-001–007. The
operations provider remains unconfigured, so no external operation is declared
executable or accepted.

Signed Resend mail ingress retains distinct envelope, provider-email, and
Beacon-delivery identities, strict inbox/customer threading, full safe mail
envelopes, and durable attachment staging/cleanup. Invalid signatures are
rejected without reserving a trusted delivery identity. Attachment recovery
retains attempt-scoped cleanup evidence across Worker loss and rolls back to an
explicit rejected placeholder when final ledger persistence fails. Inbound
mail persists automatic classification and only a persisted human Resend reply
can wake a mutable waiting Customer case; imported history and automatic mail
remain ineligible. `MAIL_FROM` is the support sender, while distinct
`NOTICE_FROM` sends operator-notification email. Signed mail addressed to the
notice mailbox is durably ignored before retrieval, and no active inbox may
reuse that mailbox. The widget requires an exact configured origin before
installation, visitor, or history lookup; it has exact CORS handling,
installation-scoped signed sessions, client-message idempotency, availability,
and zero-case inbound capture. A persisted human widget reply can use the same
case wake path without creating a case. Contact confirmation stores only a
token hash and frozen address/customer values, consumes once, and records one
winner-owned audit. Customer profile changes and block/unblock are
current-grant guarded and audited. Customer erasure has lease-bound R2 cleanup
and anonymised retained proof storage, but DATA-002 is not yet a full-clause
claim.

Current sessionless paths are exactly `/`, `/health`, `/signin`,
`/auth/verify`, `/contact/verify`, `/api/v1/auth/`,
`/api/public/mail/resend`, `/api/public/widget/availability`,
`/api/public/widget/sessions`, `/api/public/widget/messages`, `/widget/v1.js`,
`/api/public/knowledge`, `/api/public/knowledge/`,
`/api/public/receipts/`, `/receipt/`, `/_serverFn/`, `/help`, `/help/`, and
`/assets/`. Declared public reads are D1 rate-limited. Every other `api/v1`
route and every private Start handler performs its own authorization check.

Configuration is typed in `src/core/platform/configuration.ts`. Preview and
production require `BEACON_PUBLIC_ORIGIN`; production also requires support
and distinct operator-notice senders, selected outbound-provider credentials,
receipt-link secret, and rate-limit secret. Resend webhook credentials are
required for configured Resend ingress; selecting SendGrid for outbound mail
does not require unselected Resend credentials. OpenAI remains an
optional configured adapter, and the
operations provider intentionally remains unavailable until a concrete
operation/provider is chosen. Missing required configuration fails by name at
startup. The original Stage 2 wave had no paid-provider, hosted-deployment, or provider
acceptance claim; the Support pilot below records its later bounded proof.

Intercom is the sole product importer. Native v2.16 JSON/JSONL conversations
with embedded parts and archive attachments now replace the obsolete CSV
production parser; local fixtures exercise source identifiers, timestamps,
private history, attachment treatment, idempotency, and rejected diagnostics.
No authorised real Intercom export is available, so IMPORT-001 remains TODO.
Help Scout is neither a current nor planned importer claim.

## Support composition pilot — 12 September 2026

The inbox now exposes the saved reviewed-message body and an explicit dispatch
action, refreshes incoming conversation and delivery state, and shows persisted
mail attempt outcomes. The first-party widget reads live history, acknowledges
only the exact visitor session's visible outbound messages, restores replies on
reconnect, and preserves an unsent message while history changes. These paths do
not create a case. `tests/e2e/widget-client.capture.spec.ts` contains the complete
zero-case visitor → authenticated operator → reviewed dispatch → acknowledged
reply → reconnect journey; the full local browser suite passed 19/19 on Node 26.8.1.
`tests/e2e/ui-port.spec.ts` also exercises the visible unconfigured-mail failure.

The runtime consumes headless incoming-identity/replay and delivery facts from
`src/family/support`, plus optional mail evidence and Resend delivery mapping from
`src/modules/support-email`. Those files are editable edition-owned source with retained family/module
comparison origins in `edition.json` and `sources.lock.json`. Beacon keeps its conversation lifecycle and manual case
creation; shared code supplies no ticket lifecycle.

Explicit `MAIL_PROVIDER=sendgrid` selects the outbound adapter with its declared
credentials while widget ingress remains independent. Optional Resend ingress
still requires its own verified webhook configuration. A SendGrid 202 response
records provider acceptance and its message ID, not delivery; ambiguous attempts
must remain reviewable without an automatic retry. Migration `0054_provider_retry_capability.sql` stores the original submission's
retry capability before contacting a provider, and travels with full D1
export/restore. An unresolved non-idempotent attempt remains indeterminate even
if the deployment later selects another provider. Operator-notice and receipt
commands retain `dispatching` with timers cleared and an explicit indeterminate
reason; they acquire no fabricated failure time. The pilot adds no SendGrid
delivery-webhook or live Resend-ingress claim. `tests/sendgrid-platform.test.mjs`
exercises configuration, acceptance and non-retryable transport errors locally.

Hosted acceptance passed on 12 September 2026 at
[the JustEvery Beacon pilot](https://beacon-support-pilot-20260912.james-d16.workers.dev),
Worker version `12249f23-4edc-4ac8-819d-84c76ec82415`. Native bootstrap created the
owner and Support inbox. One authorised native magic-link request was accepted
by the configured SendGrid sender (HTTP 202); mailbox delivery and magic-link
exchange were not claimed. Browser authentication used a short-lived native
session fixture, revoked afterward and independently checked in D1.

The deployed Worker preserved a duplicate visitor message as one record, showed
it in the authenticated inbox, accepted the operator's separately reviewed
reply, recorded the exact visitor acknowledgement as delivered, retained zero
cases, denied unauthorised operator access (401) and a wrong visitor session
(403), and restored the reply on reconnect. JSON and PNG proof is retained at
`/tmp/runeditrun-support-deployment/beacon-live-evidence/`; its authentication
provenance explicitly distinguishes the fixture from actual mail acceptance.
The reusable caller-authorised harness is `tests/pilot/beacon-live.mjs`.

This remains a bounded pilot, not a 1.0 release. Product and baseline TODOs above
remain visible. No live Resend ingress, SendGrid delivery webhook, full email
fallback, complete declared product, or 1.0 release is established by this proof.

## Product direction now declared

- Conversation is the primary communication record with opaque internal identity. A case is separately and manually raised over one required origin conversation, with additive audited related-conversation links.
- Customer cases are customer shared and public-numbered; Back-office cases are private by default; Tracker cases are never customer shared.
- A Tracker may aggregate a widespread issue but never merges customers or authorises group customer actions. Each customer-specific operation retains its own case, target, decision, approval, command, provider outcome, and receipt.
- 1.0 channels are verified email and first-party web chat. SMS and WhatsApp are deferred. Reviewed email and first-party web-chat outbound forms are in scope; in-app announcement, mobile, guidance, survey, and autonomous automation families have the explicit dispositions in CONTRACT.md.
- A conversation-scoped reviewed send works whether a conversation has zero or linked cases, so a conversation can remain at zero cases and still reply. Its conversation-send grant and immutable per-recipient delivery snapshot create or mutate no case, action, command, or receipt. Case outcome notifications, decisions, external-provider operations, per-target receipts, and no-action resolutions remain case-scoped. A signed-in human review gates every customer send and external-provider mutation. Ordinary authorised internal CRUD remains permission-checked and audited without an action approval. Approval and provider acceptance are never success.
- Intercom export import is the single planned importer. Help Scout remains deferred and has no importer, replacement, or evidence claim.

## Explicit migration from the prior Beacon contract

The former automatic case semantics are intentionally changed. Prior CASE-002,
CHAN-015, and WIDGET-004 created or implied a case after specified inbound or
operator events. The new CASE-002, CHAN-015, and WIDGET-004 preserve the
conversation and contact capture but require a permitted member to select a
case type and record a reason. A Stage 2 migration and UX must make this
transition visible; it must not silently preserve an automatic case-creation
job.

Prior refund-only action clauses are now generic declared-operation guarantees.
They do not supply a working generic provider adapter. The Stage 2
implementation must define concrete operation types, schemas, adapters,
evidence rules, authoritative preflight, provider reconciliation, and receipts
before exposing any action as executable.

The policy surface keeps eight declarations. `approval.eligibility.v2` and
`evidence.requirements.v2` replace their v1 predecessors because their policy
decisions changed; `ai.instructions.v1` retains its prior decision, while its
stronger source and approval barriers are core contract rules.
`notification.routing.v1` retains its routing decision and records a changed
default. These are release-lineage facts; mounted defaults and narrow-only
override boundaries are exercised where the registry maps them, without
promoting unrelated policy outcomes.

## Stage 2 work required before a 1.0 claim

1. Independently accept the complete DATA-002 cascade, anonymised retained
   proof, R2 recovery, and portable-after-erasure evidence. Complete
   web-chat typing and every remaining verified-contact delivery/access
   boundary, including reviewed offline email fallback. The zero-case pilot
   now exercises reconnect and session acknowledgement locally and on the hosted pilot.
2. Complete all declared conversation and case behaviours: team/queue
   membership, merge and search semantics, tags, due dates, public
   Customer-case routes, and the non-removable Tracker boundary.
3. Complete a concrete declared operations provider/action, provider preflight,
   outcome/retry/pause handling, and full per-target receipt/delivery evidence.
   Queue/Cron composition alone is not provider acceptance.
4. Complete Knowledge and AI preparation/review/usage semantics, public and
   private content boundaries, and the full human gate. Persisted Knowledge and
   report slices are not full feature claims.
5. Complete report measures, contacts/companies/segments, notification truth,
   setup guidance, policy/extension boundaries, release workflow, and every
   remaining operational/baseline requirement with executable conformance
   evidence. Credential-free pull-request/manual CI and a fail-closed buyer
   artifact preparer exist, but they cannot package this incomplete checkpoint.
6. Prove the native Intercom importer against an authorised real export,
   including observed entity/link/attachment mapping and rejected-record
   diagnostics. Synthetic fixtures are not that proof.
7. Maintain tagged contract/registry/extension parity, measure declared limits,
   and complete clean-clone local acceptance plus deployable-artifact validation
   before calling any release 1.0. The original Stage 2 wave required no hosted deployment.

## Files and evidence

CONTRACT.md is the declaration. RELEASE.json records the semantic rewrite and is not evidence of implementation. research/conversation-desk contains only accepted prose copies (spine and Intercom edition) with the source commit; it deliberately omits the private licensed capture map, matrix, reference JSON, and images. The external Stage 1 ledgers contain clause provenance, matrix dispositions, and policy defaults for review.

## Edition source authority — 14 September 2026

`support-beacon` now owns the complete Beacon source and selected component graph.
The former family writer is retired as part of the coordinated Support cutover.
All product, UI, test and historical migration bytes are preserved from the
`0efb6d0c0b68ca7d4c1b80e31768126ecda132e4` source parent. The recipe retains
its actual Base `bc27ecf55693a68bfa7e8a6aba9fe3ce8883bcfe` and Support
`71dea496cd4e2862295b1a1e82afa6838df8f598` component origins. The independently
selected authoring tool is Base `d840a75`'s 1.1.0 runtime, including the documented
pnpm argument-separator fix; the lock records its exact byte identity.

A separate frozen composition from Support
`379a04f64def9dd46859d8faa3c2cc00f899517e` and its pinned Base
`603ee025aa5991fb44e0c3b0731f2113be85ceab` matched all application and migration
source. It differed in the historical composition lock and the SendGrid module's
sender, tests and README. This cutover preserves Beacon's existing sender; newer
marketing options and redirect rejection were not adopted as an undocumented
runtime change. The old composition lock is historical provenance, not the
current source authority.

Use the source commands in README.md to inspect, change, lock and release this
edition. Source checks and source releases do not complete the product or change
any acceptance status above. The exact conversion verification logs, frozen
comparison and portable-release evidence are retained outside product source at
`/Users/zemaj/.orchestrator/evidence/runeditrun/support-migration-20260914/beacon/`.

## Shared local onboarding — 18 September 2026

Beacon adopts Base's portable `onboarding/` source in `scripts/base-onboarding/`,
selected as a local component without claiming an unpublished upstream revision.
The adapter owns Beacon's workspace description, first inbox and due timezone,
editable lighthouse SVG and real empty local bootstrap. No password, provider or
workspace-art capability is advertised by this local adapter.

The fresh disposable-checkout acceptance harness `tests/pilot/onboarding-local.mjs`
passed missing-only setup, one approval, actual dependency installation/migrations,
first-operator bootstrap, native loopback magic-link exchange, authenticated empty
conversation list and all-prefilled review. It used no sample data, session fixture,
mail delivery or remote resources. Screenshots and result:
`/tmp/beacon-onboarding-evidence/`. Five focused tests, typecheck, seed validation
and operations describe passed. This adds no complete-product or hosted claim.

`BEACON_LOCAL_SIGNIN` separates explicit local native link delivery from sample
insertion. It requires local environment and a loopback host; hostname lookalikes
such as `127.example.test` are denied. Existing sample behavior remains tested.

The 18 September launcher acceptance also verified exact acquisition of the
current Beacon and Ferry source snapshots, dependency-free setup awaiting owner
approval, session-bound agent fill, refusal to overwrite an existing destination,
and offline resume preserving answers and owner files. Ferry additionally passed
a fresh install, native local sign-in and empty real database through the launcher.
This is local package acceptance, not public package publication or hosted product
acceptance. The next launch work must preserve these journeys while addressing
the first-release obligations in `ACCEPTANCE.md`; neither edition is release-ready.
