Skip to Content

← All archived runs

Run: onboarding-blueprint-model

run.md

Run: onboarding-blueprint-model

  • branch: claude/pipeline-onboarding-blueprint-12j318
  • pr: #603

00_intake/stub.md

Stub: Persisted onboarding blueprint model

  • feature-slug: onboarding-blueprint-model
  • epic: tenant-onboarding-wizard
  • personas: Admin, Vendor
  • initiative: Build the Bridge / objective: Q2-2026 O1 — Establish PMF with Vendor Partners
  • depends-on: none
  • sequence: 1 of 7

Problem

The demo holds the whole onboarding draft in client state; nothing survives a refresh. A mandatory redirect-until-complete wizard (this epic's end state) is only viable if the draft and the tenant's onboarding progress are persisted — the first user must be able to leave and resume, and the platform must be able to ask "is this tenant set up?" cheaply on every request. Issue #588 Q3 (persist vs ephemeral) and Q5 (confidence/provenance) are settled here: persist both.

Proposed change

A first-class, tenant-scoped onboarding blueprint document plus a tenant-level onboarding lifecycle. The blueprint spans the 7 areas from the demo spine (company profile, categories, products, services, skills, SLA tiers, settings); every generated item carries confidence, provenance (AI-drafted vs human-edited) and a review state (pending / approved / edited / rejected). The blueprint itself moves through a status lifecycle (e.g. draft → in-review → verified → committed), and the tenant records its onboarding state so gating (stub 7) and the wizard can resolve progress without loading the blueprint. Includes the service-layer CRUD for the blueprint (get/upsert section, set item review state, advance status) — no UI, no AI.

Acceptance criteria (rough)

  • A blueprint document can be created, read section-by-section, updated and advanced through its status lifecycle via the service layer, scoped to one tenant.
  • Every blueprint item stores confidence, provenance and review state, and survives process restarts (persisted in MongoDB).
  • The tenant model/service exposes an onboarding state that answers "setup done/verified/completed?" without loading the blueprint document.
  • Freshly created tenants start in a not-onboarded state; the existing tenant-defaults seed still applies.

Out of scope (this feature)

  • Any AI generation or source analysis (stubs 4–5).
  • Committing the blueprint into the real services (stub 3).
  • UI and routing/redirects (stubs 6–7).

Notes for Define

Follow the house Mongoose idiom (plugin trio, tenant-leading indexes, bounded arrays — mind the 7-section document size; sections with many items may need capping or sub-documents). touches: packages/services/src/db/models/, db/services/, db/services/tenant/, a migration via db-migration. The demo's section keys in apps/demo/lib/mock/onboarding.ts (profile|categories|products|services|skills|sla|settings) are the section vocabulary.

01_define/output/spec.md

Spec: Persisted onboarding blueprint model

  • slug: onboarding-blueprint-model
  • personas: Admin, Vendor
  • touches: packages/services/src/db/models/ (new onboarding-blueprint model + tenant model change), packages/services/src/db/services/ (blueprint service), packages/services/src/db/services/tenant/ (onboarding-state service), packages/services/src/db/seed/tenant-defaults.ts, a migration via db-migration
  • complexity: standard

Problem

The AI onboarding demo (apps/demo/app/onboarding/) holds the entire onboarding draft in client state — nothing survives a refresh. The productionised concierge wizard this epic builds is mandatory and redirect-until-complete for fresh tenants, which is only viable if two things are durable: the tenant's onboarding draft (so the first user can leave and resume) and the tenant's onboarding progress (so the platform can cheaply answer "is this tenant set up?" on every request without loading the draft). This is stub 1 of the tenant-onboarding-wizard epic (sequence 1 of 7, depends-on: none) — the persistence foundation every later stub builds on. It advances Build the Bridge / Q2-2026 O1 — Establish PMF with Vendor Partners (KR: onboard 8+ vendors onto paid tiers): no persisted setup, no repeatable vendor onboarding. Issue #588's Q3 (persist vs ephemeral) and Q5 (confidence/provenance) are settled here: persist both.

Proposed change

Add a first-class, tenant-scoped onboarding blueprint document plus a tenant-level onboarding lifecycle, and the service-layer CRUD to drive them. No UI, no AI, no committing into real services this run.

Onboarding blueprint document — one per tenant, spanning the 7 sections from the demo spine: profile, categories, products, services, skills, sla, settings (the exact section keys from apps/demo/lib/mock/onboarding.ts). The document carries:

  • A status lifecycle: draft → in_review → verified → committed.
  • Per section, a list of items; every item carries the common metadata mandated below plus a section-specific payload. Item metadata (all required):
    • confidence — number 0–100 (matching the demo's confidence scale).
    • provenanceai_drafted | human_edited.
    • reviewStatepending | approved | edited | rejected.
  • Item arrays are bounded (house idiom) so the 7-section document stays lean and cannot grow unboundedly; the per-section cap and whether large sections become sub-documents is Build's call, but a cap must exist.

Tenant onboarding state — a coarse onboarding status recorded on the tenant (embedded, not a separate lookup) so gating (stub 7) and the wizard can resolve progress with a cheap tenant read and without loading the blueprint. Status enum: not_started → in_progress → completed. Freshly created tenants start not_started.

Service layer (@sustentus/services/server, following the house Mongoose idiom — plugin trio, tenant-leading ESR indexes, async model factory): get blueprint; read/upsert a single section; set an item's review state; advance the blueprint status; and get/set the tenant onboarding state. Every call is tenant-scoped.

Seed — the existing tenant-defaults seed continues to apply; fresh tenants default to not_started onboarding state. A migration (via db-migration) creates the collection/indexes and backfills existing tenants to a sensible onboarding state.

Acceptance criteria

  • A blueprint document can be created, read section-by-section, updated (upsert a section, set an item's review state) and advanced through its status lifecycle (draft → in_review → verified → committed) via the service layer, all scoped to a single tenant.
  • Every blueprint item stores confidence (0–100), provenance (ai_drafted | human_edited) and reviewState (pending | approved | edited | rejected), and the whole document survives a process restart (persisted in MongoDB, verified by re-reading after reconnect).
  • The tenant exposes an onboarding state (not_started | in_progress | completed) that answers "setup done/verified/completed?" via a tenant read without loading the blueprint document.
  • Freshly created tenants start in not_started; the existing tenant-defaults seed still applies unchanged, and a migration backfills existing tenants.
  • Blueprint section item arrays are bounded (a cap exists) and the model uses the house idiom (plugin trio, tenant-leading index, async model factory); the blueprint is uniquely keyed per tenant.

Out of scope

  • Any AI generation or source analysis (stubs 4–5).
  • Committing/atomically applying a verified blueprint into the 7 real services (stub 3).
  • Concierge conversation, chat route, or any AI agent wiring (stubs 2, 5).
  • UI, the /onboarding surface, and routing/redirect gating (stubs 6–7).
  • Migrating or re-onboarding existing tenants beyond the one-off backfill (epic-level out of scope).
  • Retiring the /admin/settings/setup static checklist.

Open questions

  • none. (Modelling details deliberately left to Build: exact per-section payload sub-schemas, the numeric item cap, and whether the tenant onboarding state is a single enum field or a small nested object — none of these change the observable outcomes above.)

02_build/output/notes.md

Build notes: onboarding-blueprint-model

  • commits: feat: onboarding-blueprint-model — persisted blueprint model, tenant onboarding state, service CRUD + migration

What changed

  • packages/services/src/db/models/onboarding-blueprint.ts (new): the tenant-scoped onboarding_blueprints collection. One document per tenant, status lifecycle (draft → in_review → verified → committed), and a sections array over the 7 demo-spine keys (profile · categories · products · services · skills · sla · settings). Each section holds a bounded items array; every item carries confidence (0–100), provenance (ai_drafted | human_edited) and reviewState (pending | approved | edited | rejected), plus a flexible data payload for section-specific fields. House idiom: plugin trio (schema → softDelete → tenant), async getOnboardingBlueprintModel() factory, tenant-leading unique partial index { tenantId: 1 } (isDeleted:false), MAX_ITEMS_PER_SECTION = 200 array-length validator.
  • packages/services/src/db/models/tenant.ts: added the embedded onboarding.status field (not_started | in_progress | completed, default not_started) and the TENANT_ONBOARDING_STATUSES enum — a cheap onboarding read that never loads the blueprint.
  • packages/services/src/db/services/onboarding-blueprint/{index,instance}.ts (new): OnboardingBlueprintServicegetBlueprint, getOrCreateBlueprint, getSection, upsertSection, setItemReviewState, advanceStatus (forward-only). Registered in the services barrel.
  • packages/services/src/db/services/tenant/index.ts: getOnboardingStatus (projects only onboarding, no blueprint read) and setOnboardingStatus on TenantService.
  • packages/services/src/db/models/index.ts: exported the new model.
  • packages/services/src/db/migrations/1783425600000-onboarding-blueprint-and-tenant-onboarding.ts (new): creates the blueprint unique partial index and backfills existing tenants to onboarding.status = "not_started". Symmetric down.

Acceptance criteria status

  • Blueprint created / read section-by-section / updated / status-advanced via the service layer, tenant-scoped — OnboardingBlueprintService methods, all keyed on tenantId.
  • Every item stores confidence/provenance/reviewState and persists in MongoDB — required schema fields on BlueprintItemSchema; the document round-trips through the onboarding_blueprints collection.
  • Tenant exposes an onboarding state answering "done/verified/completed?" without loading the blueprint — tenantService.getOnboardingStatus reads tenant.onboarding.status via .select("onboarding").
  • Fresh tenants start not_started; the tenant-defaults seed is unchanged; migration backfills existing tenants — schema default (applied on insert via setDefaultsOnInsert) + the migration's updateMany. The seed needed no edit, so it "still applies unchanged" by construction.
  • Section item arrays bounded (cap MAX_ITEMS_PER_SECTION); house idiom (plugin trio, tenant-leading index, async factory); blueprint unique per tenant.

Verify result

  • mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here. Release reads them back from the PR's check runs. No check expected to fail.

Notes for review

  • Seed left untouched by design. AC4 says the tenant-defaults seed "still applies unchanged"; fresh-tenant not_started comes from the schema default, and existing tenants are handled by the migration backfill — so no seed edit was needed. Flagging in case a reviewer expected a seed diff.
  • Blueprint stays a single document. Sections are embedded (not a child collection) with a hard 200-item-per-section cap — 7 sections × 200 keeps the doc well under the audit's 1,000-element bound while matching the "read the whole blueprint beside the chat" access pattern the concierge (stub 6) needs. If a real catalogue ever outgrows this, the sections are the natural split point.
  • data is Schema.Types.Mixed so each section keeps its own payload shape (pricing, SLA tiers, …) without seven bespoke sub-schemas; the typed metadata (confidence/provenance/reviewState) is enforced uniformly. This was the modelling latitude the spec explicitly left to Build.
  • upsertSection is two writes (in-place $set on the positional match, else $push) because the positional operator can't upsert an array element that may not exist yet.

03_release/output/release.md

Release: onboarding-blueprint-model

  • pr: #603 (https://github.com/sustentus/sustentus/pull/603) · merged: yes — 2026-07-07 (squash)
  • CI: green — Quality Project (format/lint/typecheck), Review diff against CONVENTIONS.md, Audit database, Migrate preview database all passed. One earlier red (format:check on a non-idempotent prettier wrap in spec.md) was fixed by reflowing the acceptance criterion; re-run went green.
  • technical docs: no technical docs impact — the change adds a Mongoose model, service methods and a migration following existing documented conventions; no new app/package/route/env var/build step, no new @sustentus/services entrypoint. technical/packages/services describes structure generically (no per-model/collection inventory), so nothing there goes stale.
  • business docs: no business docs impact — backend-only foundation, no user-visible behaviour (no UI, route, or persona capability changed).
  • release notes: none — internal change (no end-user changelog: nothing users can do differently yet; investor update skipped by decision — announce when the wizard ships, epic stubs 6–7).
  • deploy: production DB migration (db-migrate workflow) + app deploys confirmed via poll-deploy.sh post-merge — see below.
  • sent: none — no investor email this release (internal foundation).

Review summary

  • Claude Code Review (CI "Review diff against CONVENTIONS.md") passed with no inline comments; no cleanup surfaced. Diff is model + services + migration + run files only — no dead code or debug.

Acceptance check (vs spec)

  • Blueprint created / read section-by-section / updated / status-advanced via the service layer, tenant-scoped — OnboardingBlueprintService (get, getOrCreate, getSection, upsertSection, setItemReviewState, advanceStatus), all keyed on tenantId.
  • Every item stores confidence/provenance/reviewState and persists in MongoDB — required fields on BlueprintItemSchema; round-trips through onboarding_blueprints.
  • Tenant exposes onboarding state without loading the blueprint — tenantService.getOnboardingStatus reads tenant.onboarding.status via .select("onboarding").
  • Fresh tenants start not_started; tenant-defaults seed unchanged; migration backfills existing tenants — schema default (setDefaultsOnInsert) + migration updateMany.
  • Section arrays bounded (MAX_ITEMS_PER_SECTION); house idiom (plugin trio, tenant-leading unique index, async factory); one blueprint per tenant.