Skip to content

Onboarding: each tenant gets Myra and the core workflows - #708

Open
TheGreatAxios wants to merge 14 commits into
mainfrom
cl-7584-onboarding-each-tenant-gets-myra-core-workflows
Open

Onboarding: each tenant gets Myra and the core workflows#708
TheGreatAxios wants to merge 14 commits into
mainfrom
cl-7584-onboarding-each-tenant-gets-myra-core-workflows

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Linear: CL-7584

Summary

Onboarding is now a per-tenant desired-state install, driven by a client-side document — no hub table, no boot seeding, no per-user Myra.

  • Desired-state document. TENANT_DESIRED_STATE in packages/onboarding/src/desired-state.ts pins this build's stack (Myra via DEFAULT_WORKFLOWS, the required tool packages, the default skills) by reference to the existing single-source constants. Adding a core workflow is a data edit the reconciler honors per tenant.
  • Reconcile engine. reconcileTenantDesiredState compares a tenant's native workflow assets/deployments, tool registry, and skills to the document and installs only what is missing (tools → skills/grants/workflows via seedTenant, deploy gated on the tenant's resolved catalog offerings). A converged tenant gets a reads-only pass — proven by a zero-non-GET second-pass test. Sidecar-unavailable pins report blocked (honest copy, retry later); real failures report failed and re-run safely.
  • Three triggers, no new job. (1) The existing pending_seed drain delegates to reconcile (row semantics unchanged). (2) A tenant-create observer fires a fire-and-forget reconcile on POST /api/tenants 201 (in-process dedupe; 403 fires none) — a second tenant gets its own Myra. (3) The provision probe kicks reconcile when pins are pending; joined members included.
  • Visible setup steps. GET /api/onboarding/provisioning-status carries doc-derived steps; the onboarding page renders them and collapses when ready.
  • Shutdown safety. Hub close stops the observer and bounds the pool close so a fire-and-forget reconcile can never stall teardown.

A second user in a tenant joins as a member and deploys nothing — one Myra per tenant.

Test plan

  • packages/onboarding suites (desired-state, reconcile idempotency, drain, routes) — green
  • apps/hub — signup-genesis 8/8, tenant-create-onboard green, boot-does-not-seed still proves boot seeds nothing
  • bun test scripts/e2e/local-rip.test.ts — fresh hub → signup → connect yields Myra without boot seed; second user, second tenant, and revisit legs green
  • bun run check — exit 0

The document is plain client-side data composed by reference over
DEFAULT_WORKFLOWS, REQUIRED_SEED_TOOL_PACKAGES, and DEFAULT_SKILLS — no
hub table and no migration. readTenantDesiredStateStatus reads a
tenant's real workflow, tool-package, and skill state against it using
native GETs only. Also adds fetchRegistryTarballSource and
installRegistryTarball for the (tested but unpopulated) tarball-url
source kind, and re-exports the defaults through @corbits/seeding.
Installs only absent pins, tools first, then skills, grants, and
workflows together through seedTenant with confirmDeployments false.
A converged tenant reconciles with reads only: seedTenant is never
entered and the registry publish is gated on the seeded check.
Sidecar-unavailable (502-class) pins report blocked without throwing;
other failures report failed and are safe to re-run. Model resolution
picks the tenant's top-priority resolved catalog offering, inherited
included.
runOnce no longer sequences publish, fully-seeded check, and
ensureSeeded itself; it delegates to the doc-driven reconcile. Row
semantics are unchanged: a ready report clears the pending_seed row, a
blocked report keeps it for the next pass, and a failed report keeps it
and counts for backoff.
POST /api/onboarding/provision fires a fire-and-forget desired-state
reconcile kick when the caller's tenant still has pending pins — this is
how a joined member's bench converges. GET
/api/onboarding/provisioning-status now carries the doc-labeled step
list the onboarding page renders; the ready/provisioning gate stays on
the workflow set.
An outer wrap beside the tenant-create guard watches the native
POST /api/tenants route: a 201 fires one fire-and-forget desired-state
reconcile for the new tenant under the creator's minted session, deduped
per tenant in process. A tenant with no catalog offerings reports the
workflow pins blocked in the log instead of throwing. The onboarding
provision route's revisit kick shares this reconciler.
The onboarding page polls complete-setup while pins are pending, renders
the hub's doc-labeled steps under the loader, and collapses the list and
hands off to the app once the answer is ready.
The observer is hoisted so hub close can stop it and wait (bounded) for
in-flight reconciles before the pool ends, and kicks replay the
creator's cookies instead of minting a session. Fire-and-forget
reconcile catches document why they report through pin status instead
of the error sink.
@linear-code

linear-code Bot commented Sep 10, 2026

Copy link
Copy Markdown

CL-7584

Non-sidecar reconcile failures now reach reportError instead of dying
as log lines; the blocked branch keeps already-present pins honest; the
observer's shutdown contract is documented as what it actually is;
debug residue and dead code from the first pass are gone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant