mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 19:35:13 +01:00
Paperclip Cloud collects a mission, a first agent and a first task during signup and pushes them into the tenant stack at activation, as `POST /api/companies/:companyId/onboarding-seed`. Nothing served that route, so the tenant answered 404, Cloud recorded the push as unacknowledged, and a freshly activated stack opened on the empty first-run wizard with the answers the customer had already given stranded on the Cloud side. This adds the receiver. Contract. The body carries a content-addressed `revision` plus optional `mission`, `agent` (`name`, `role`) and `firstTask` (`title`, `details`). It is customer free text, so it is validated and bounded in `packages/shared` and never read from a header — the `x-paperclip-cloud-*` set is the trusted identity envelope, derived server-side from host plus verified domain records, and mixing user content into it would undermine exactly that property. A test plants a mission on a cloud header and asserts the body wins. Application. The mission becomes the company-level goal, the agent becomes the company's first hire (its free-text role lands on `title`; the structural `role` stays `ceo`, which is what the org chart and the default-instructions lookup read), and the first task becomes an issue in the Onboarding project assigned to that agent — the same shapes the first-run wizard produces, so a seeded stack and a manually onboarded one are indistinguishable afterwards. The seeded agent is created on `claude_local`, mirroring the teams-catalog default for agents created server-side without a human running an environment test first, and overridable via `PAPERCLIP_ONBOARDING_SEED_ADAPTER_TYPE`. Idempotency. Cloud retries until it gets a 2xx and treats any 2xx as "the tenant holds this content", so the new `company_onboarding_seeds` table records the applied revision along with the goal, agent and issue it produced. Replaying a revision that already matches is a successful no-op; a later revision — the customer edited their answers — updates those three rows in place rather than creating a second agent and a second task. The record is written last, after every other write has landed, so a partial application cannot present itself as acknowledged. Ordering, not eventual consistency. Everything is applied before the 200 is sent. The tests read the database immediately after the response with no waiting and no polling, so a lazy receiver fails them on a fast machine as well as a slow one. That matters because the redirect into the tenant dashboard is gated on this acknowledgement. Tested with `pnpm typecheck` (whole workspace) and `vitest run server/src/__tests__/onboarding-seed-route.test.ts server/src/__tests__/openapi-routes.test.ts` — 14 passing, including the happy path, replay, revision update, project reuse, the header-injection guard, the field bounds, and the 403 for a caller without access to the company. The suite runs against embedded Postgres, so the new migration is exercised too. Co-authored-by: Paperclip <noreply@paperclip.ing> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>