company-profile-tenant-modelrun.md00_intake/stub.mdThe company-profile blueprint has no real home. The Tenant model has no typed profile fields (only
name, slug, imageUrl plus untyped catch-alls), so at commit the profile section is dumped into an
untyped tenant.settings.profile object. The AI invents arbitrary profile datapoints that never become
real, typed tenant data.
Add typed company-profile fields to the Tenant Mongoose model (candidate set: website, description,
industry, logo — reusing/formalising imageUrl, company size, HQ/location, tagline) with a reversible
migration. Then align the profile blueprint so its extraction-contract data shape, the concierge
prompt, the review-panel editable fields, and the commit step all target those typed fields — writing
them onto the Tenant document instead of settings.profile. The AI drafts these from the analysed
website/source where possible.
data shape matches the new tenant fields (no free-form junk).settings.profile.mongoose-model + db-migration skills for the Tenant change; keep the house idiom
(plugin trio, tenant-leading indexes).packages/services/src/db/models/tenant.ts, a migration under the packages/services
migrations dir, packages/services/src/ai/onboarding/{contract.ts,prompt.ts},
packages/services/src/db/services/onboarding-blueprint/commit.ts (the profile branch, which today
writes settings.profile.<key> + name), apps/web/components/onboarding/types.ts (profile
SECTION_META fields).01_define/output/spec.mdThe AI onboarding concierge drafts a per-tenant blueprint whose company profile section has no
real home. The Tenant model carries only name, slug, and imageUrl plus untyped catch-alls
(settings, publicMetadata, privateMetadata), so at commit the profile is dumped into an untyped
settings.profile.<key> blob (see commit.ts §6). Because there are no typed target fields, the AI is
free to invent arbitrary profile datapoints that never become real, queryable tenant data. This is
step 2 of the onboarding-blueprint-alignment epic (depends-on remove-categories-section, already
merged — categories is gone from the contract), which advances the Build the Bridge initiative,
objective Q2-2026 O1 — Establish Product-Market Fit with Vendor Partners (KR: onboard 8+ vendors onto
paid subscription tiers): a vendor's onboarding should produce real, trustworthy tenant data, not
free-form junk.
Give the company profile a typed home on the Tenant model and align the blueprint's profile section
to it end to end.
profile sub-document (mirroring the existing typed onboarding
sub-doc idiom) with optional string fields: website, description, industry, companySize,
location, tagline. name stays the top-level required field; the company logo reuses the
existing imageUrl field (no new logo field). Keep the house model idiom (plugin trio, no new
index needed — these fields are not query keys).ts-migrate-mongoose migration under packages/services/src/db/migrations
that initialises profile: {} on existing tenants (up) and $unsets it (down), following the
repo's migration template. No profile content is backfilled.data to exactly those
typed fields, and instruct the concierge to draft only those fields from the analysed
website/source, so no invented datapoints are emitted.commitVerifiedBlueprint to write the typed fields:
name → tenant.name, logo → tenant.imageUrl, and website / description / industry / companySize / location / tagline → tenant.profile.<field>. Unknown keys are ignored (whitelist),
so nothing lands in settings.profile anymore.SECTION_META.editableFields (apps/web) from the
current name + tagline to the real typed field set, so a reviewer edits the actual profile
fields inline before launch.Tenant model gains a typed profile sub-document with optional fields website,
description, industry, companySize, location, tagline; name remains the top-level
field and the logo remains imageUrl (no new logo field).ts-migrate-mongoose migration adds profile: {} to existing tenants on up and
removes it on down, following the repo's migration template (db:migrate runs clean both ways).data shape matches the new typed tenant fields, and the
concierge prompt drafts only those fields — no free-form invented datapoints.tenant.name,
tenant.imageUrl for the logo, tenant.profile.<field> for the rest) and not into
settings.profile; unrecognised keys are ignored rather than persisted.name, tagline, website, description, industry, companySize, location).profile: {} default.conversational-progressive-flow, epic step 6).imageUrl), and document shape (nested typed profile sub-doc) were settled with
the user during Define.02_build/output/notes.mdpackages/services/src/db/models/tenant.ts: added TENANT_PROFILE_FIELDS (the single source of truth: website, description, industry, companySize, location, tagline), TenantProfileField/TenantProfile types, a profile? field on ITenant, and a TenantProfileSchema sub-document derived from the field list (so it can't drift; strict mode drops anything else). name stays top-level; the logo stays imageUrl (no new field).packages/services/src/db/migrations/1783598400000-company-profile-tenant-fields.ts: reversible migration — up sets profile: {} on tenants missing it (idempotent), down $unsets profile. Raw driver, tenants collection, symmetric. No content backfill, no index.packages/services/src/ai/onboarding/contract.ts: gave the profile section a typed data shape (ProfileDataSchema = name + the typed tenant fields, all optional). Unknown keys are stripped on parse, so the extractor can't invent free-form profile datapoints. DraftBlueprintSchema now maps profile to the typed items; the satisfies guard relaxed to a Zod-array-per-key so sections can carry different item shapes while still failing to compile if a section is missing.packages/services/src/ai/onboarding/prompt.ts + analyse-source.ts: the concierge prompt and the extraction system prompt now enumerate exactly the typed profile fields ("using only these fields") so drafting targets them. analyse-source widens each section to DraftBlueprintItem[] for the uniform map (profile now has a narrower data type).packages/services/src/db/services/onboarding-blueprint/commit.ts: rewrote the profile commit branch. It now writes name → tenant.name and each TENANT_PROFILE_FIELDS value → tenant.profile.<field>, merged across profile items into one $set. It is a whitelist — only the known typed keys are read, so stray keys are ignored — and it no longer writes settings.profile.* at all.apps/web/components/onboarding/types.ts: expanded the profile section's SECTION_META editableFields from name+tagline to the full typed set (client-safe mirror; this app can't import the server barrel).profile sub-document (website, description, industry, companySize, location, tagline); name top-level, logo stays imageUrl — tenant.ts.ts-migrate-mongoose migration adds profile: {} on up, removes on down, following the template — 1783598400000-company-profile-tenant-fields.ts.data shape matches the typed fields and the concierge prompt drafts only those fields — contract.ts (ProfileDataSchema), prompt.ts, analyse-source.ts.tenant.name, tenant.profile.<field>) and not settings.profile; unrecognised keys ignored — commit.ts.types.ts SECTION_META.profile.analyse-source.ts typed extraction into the same fields; empty/thin paths unchanged.block-local-checks hook enforces that. Release reads them back from the PR's checks.up/down can't be applied in the sandbox (DB unreachable); CI's db-migrate workflow applies it on merge. create also needs MONGODB_URI, so the file was authored by hand from migrations/template.ts (timestamp after the latest existing migration).draft_section tool (blueprint-tools.ts) keeps its single section-agnostic item schema, so its profile data isn't statically typed to the profile shape — deliberately, to avoid reshaping a shared tool. The commit whitelist + the prompt constraint still guarantee only typed fields reach the tenant; the typed contract covers the primary analyse_source auto-draft path.TenantProfileSchema is derived from TENANT_PROFILE_FIELDS so the schema and the field list can't drift; the commit whitelist iterates the same const.03_release/output/changelog.mdWhen you set up a new workspace, the setup assistant now captures your company profile as real, structured details — website, description, industry, company size, location, and tagline.
03_release/output/investor-update.mdWho it's for: Vendors onboarding onto the platform (and admins) What shipped: During AI-guided setup, a vendor's company profile — website, industry, size, location, tagline — is saved as structured company data, drafted from their own website. Why it matters: A more credible, complete setup advances our Q2 objective to establish product-market fit with vendor partners.
Dig deeper: <merged-PR URL> · <changelog entry URL>
03_release/output/release.mdtechnical/packages/services describes the concierge at the "six sections → commits into the real workspace" level, which this change doesn't alter (typed-field/storage detail is below its granularity)business/platform-overview describes onboarding as "a setup blueprint (profile, catalogue, skills, SLAs, settings) fills in beside them, then reviewing each drafted item before launching"; still accurateapps/help/app/changelog/2026-07-08-company-profile-tenant-model) + investor draft in this PRnotes.md and a duplicate migration timestamp prefix — both fixed on the branch (single-line notes; migration renamed to 1783598400000-…). Bot re-reviewed and marked ISSUE_RESOLVED.Quality Project failure was format:check on the build notes only — no code involved; fixed.profile sub-document (website, description, industry, companySize, location, tagline); name top-level, logo stays imageUrl — tenant.ts.profile: {} on up, removes on down — 1783598400000-company-profile-tenant-fields.ts.data shape matches typed fields; concierge/analyser prompts draft only those — contract.ts, prompt.ts, analyse-source.ts.tenant.name, tenant.profile.<field>), not settings.profile; unknown keys ignored — commit.ts.apps/web/components/onboarding/types.ts.analyse-source.ts.