product-conversationrun.md00_intake/stub.mdThe onboarding concierge already crawls a website (analyse_source), drafts confidence-scored items
into the onboarding-blueprint document, and commits products as name + description into the thin
product model — but everything else the conversation learns about a product lands in the blueprint
item's free-form data payload and is then never used. The BRD agent that will consume product
knowledge sees three strings. The MVP needs the conversation to actually acquire a rich, typed,
confidence-scored product profile — with no new DB model — by constraining what the blueprint's
product data holds and driving a gap-filling conversation over it.
Three coupled backend changes in packages/services/src/ai/onboarding/, all writing into the existing
blueprint item data payload (no schema work):
contract.ts): replace the free-form products-section payload with a typed
ProductProfileDataSchema (Zod, all fields optional) — e.g. overview, targetCustomers,
useCases, keyFeatures, pricingModel, integrations, differentiators, docsUrl,
supportChannels, faq. Enforce it at the persistence boundary exactly as services/skills are
today (CONSTRAINED_SECTION_FIELDS / sanitiseSectionData) so both write paths stay in lockstep.analyse-source.ts): point product extraction at the typed profile schema so a
product URL yields a substantially filled profile draft with per-item confidence — the interview then
only mops up the gaps.prompt.ts, blueprint-tools.ts): after analyse_source drafts
profiles, run a per-product gap interview that asks only about fields the analysis left empty or
low-confidence, one product at a time, instead of the generic section walkthrough for products. A
data-driven gap computation (which fields are missing/low-confidence per product) is exposed to the
model so "what to ask next" is data-driven, not prompt-vibes. Extend the tool set with a product-
focused update path — either draft_section accepting the typed payload, or a dedicated
update_product_profile tool on the same defineTenantTool infra. Vendor answers land as data
updates with confidence 100 (vendor-stated) and conversational provenance.getProductKnowledge read service — product-knowledge-read-seam (stub 2).product-profile-review-panel (stub 3).data.The field list is the contract to nail here — finalise it against what the BRD agent will actually
read (see the epic's brd-agent-upgrade.md); the schema shape is what we're buying, individual fields
are cheap to adjust later. Both write paths (analyse_source and the conversational tool) must funnel
through the same sanitiser so they cannot drift. The "low-confidence" threshold that drives the gap
computation needs a concrete number set in Define. touches:
packages/services/src/ai/onboarding/{contract.ts, analyse-source.ts, prompt.ts, blueprint-tools.ts}.
01_define/output/spec.mdThe onboarding concierge already crawls a vendor's website URL (or a pasted description) via
analyse_source, drafts confidence-scored items into the onboarding-blueprint document, and commits
products as name + description into the thin product model — but everything else the
conversation learns about a product lands in the blueprint item's free-form data payload and is then
never used. Two gaps follow. First, the BRD agent that will later consume product knowledge
(brd-agent-upgrade, epic stub 9) sees only three strings. Second, the interview treats products as
a single section — it presents the crawled product list, asks one blanket question, and moves on — so
a vendor with many products is never actually grilled about each one. This is the first stub of the
vendor-onboarding-mvp epic (Build the Bridge / Q2-2026 O1 — Establish Product-Market Fit with
Vendor Partners): it makes the onboarding conversation acquire a rich, typed, confidence-scored
profile for every product the vendor brings — with zero schema change — so the committed
blueprint becomes the interim product-knowledge store the rest of the epic reads from.
AnalysisSource is {kind:"url"} (the vendor's
website) or {kind:"text"} (a pasted description). analyse_source crawls that single source and
returns a products array — one candidate per product it found. There is no per-product URL to
analyse, and Build must not assume one.MAX_ITEMS_PER_SECTION cap on a section's items.Three coupled backend changes in packages/services/src/ai/onboarding/, all writing into the existing
blueprint item data payload — no Mongoose model added or changed:
Typed contract (contract.ts): add a typed ProductProfileDataSchema (Zod, every field
optional) for the products section, mirroring how ProfileDataSchema types the profile section
today. The products slot in DraftBlueprintSchema changes from free-form sectionItems to items
whose data is ProductProfileDataSchema. Enforce the same field set at the persistence boundary by
adding products to CONSTRAINED_SECTION_FIELDS, so sanitiseSectionData strips off-contract keys
on both write paths (crawl and conversation) exactly as services/skills are today. Each field
is per-product; the schema types one product's profile, and a blueprint holds one such item per
product.
The locked field set (all optional):
| field | meaning |
|---|---|
overview |
what the product is, in a sentence or two |
targetCustomers |
who it is for |
useCases |
primary problems it solves / jobs it does |
keyFeatures |
headline capabilities |
pricingModel |
how it is priced (tiers/seat/usage/quote) |
integrations |
systems it connects to |
differentiators |
why it wins vs. alternatives |
docsUrl |
link to product documentation |
supportChannels |
how customers get help |
faq |
notable Q&A the vendor volunteers |
Richer crawl (analyse-source.ts): point the products extraction at ProductProfileDataSchema
so crawling the vendor's one website URL (or description) yields, for each product found, a
substantially filled profile draft — every field carried on a confidence-scored item. The extraction
system prompt is extended to fill the typed product fields (not just name + description). The
per-product interview then only mops up the gaps.
Per-product gap interview (prompt.ts, blueprint-tools.ts): replace the single
products-section walkthrough with a loop that iterates over every product — each one the crawl
drafted, plus any the vendor adds in conversation — and grills each in turn. For the current
product, a data-driven gap computation (exposed to the model) lists which of its fields are empty
or below the gap threshold, and the concierge asks only about those, one product and one question at
a time, until that product's gaps are filled or the vendor skips a field. Only when all products
are covered does the interview advance to the next section (services). Vendor answers write through a
dedicated update_product_profile tool (built on the existing defineTenantTool infra, same as
draft_section) that takes the typed payload for one named product, landing answers as data
updates at confidence 100 (vendor-stated) with conversational provenance. The profile, services,
skills, sla and settings walkthroughs are unchanged.
< 70. Fields the crawl supported strongly (≥ 70) are kept without
re-asking; vendor answers always land at 100. (Steer by editing this number.)brd-agent-upgrade will read (product
knowledge to ground the requirements conversation). The schema shape is the deliverable; individual
fields are cheap to add later.MAX_ITEMS_PER_SECTION. The interview grills each product the
crawl found and any the vendor adds, up to the existing per-section item cap — it does not silently
stop at the first product.update_product_profile tool, not an overload of draft_section — keeps
the per-product gap loop self-documenting and leaves the generic section tool untouched. Both funnel
through sanitiseSectionData so the two write paths cannot drift.analyse_source and the conversational update_product_profile tool) — an invented field
never reaches the stored blueprint.getProductKnowledge read service — product-knowledge-read-seam
(epic stub 2).product-profile-review-panel (epic stub 3).vendor-onboarding-redesign epic.data.MAX_ITEMS_PER_SECTION cap — the interview grills up to the existing bound.spec.md to change any before ticking Spec approved.)02_build/output/notes.mdAll in packages/services/src/ai/onboarding/ — no Mongoose model or migration, per spec.
contract.ts — added ProductProfileDataSchema (Zod, all fields optional): the 10 rich
PRODUCT_PROFILE_FIELDS plus name + description (kept because the product commit reads
data.name/data.description into the thin product model — commit.ts — and that path is out of
scope to change). Pointed DraftBlueprintSchema.products at the typed items and added products to
CONSTRAINED_SECTION_FIELDS so sanitiseSectionData strips off-contract keys on every write path.
Added GAP_CONFIDENCE_THRESHOLD = 70 and a pure computeProductGaps(item) helper returning, per
product, the empty fields (missing), the filled fields, and whether item confidence is below
threshold (lowConfidence) — the shared definition of "gap".analyse-source.ts — extended the extraction system prompt's products bullet to fill the typed
profile fields for each product (not just name + description). No per-product URL: the single
vendor source (website URL or pasted description) yields the product set, unchanged.blueprint-tools.ts — two new tenant tools on the existing defineTenantTool infra:read_product_gaps (read-only) — lists each product with its computeProductGaps breakdown, so
the model drives the interview from data, not prompt-vibes.update_product_profile (write) — the products counterpart to draft_section: merges the vendor's
stated fields for one product into its profile at confidence 100 with human_edited provenance
(the conversational, vendor-stated marker vs the ai_drafted crawl), only merging fields actually
given (no undefined-clobber), funnelled through sanitiseSectionData. An unknown key is added, so
a vendor can name a product the crawl missed. Both registered in buildOnboardingConciergeTools.prompt.ts — described the rich product profile in the six-section list, and replaced the single
products walkthrough with a per-product gap loop: call read_product_gaps, grill one product at a
time about only its missing/low-confidence fields (one question per message), record via
update_product_profile, add vendor-named products, and advance to services only once every product
is covered. The other five sections' walkthrough is unchanged.analyse-source.ts
system prompt fills the typed fields; DraftBlueprintSchema.products is the typed schema so the
crawl produces one profile item per product.read_product_gaps + update_product_profile + the prompt.ts loop.update_product_profile
adds an unknown key; the prompt instructs adding vendor-named products.sanitiseSectionData("products", …)
in analyse-source.ts (toCandidates) and in update_product_profile.prompt.ts override is products-only; draft_section
untouched for the other sections.onboarding-blueprint.ts). "Low-confidence field" is
therefore modelled as: an empty field is always a gap; when the item's overall confidence is < 70
its filled fields are also flagged for confirmation. This is the schema-free reading of the spec's
threshold — no per-field confidence was added.name + description retained in the products contract on purpose — the commit path (out of
scope, stub 2) reads them. Constraining products to only the 10 rich fields would have silently
dropped product descriptions at commit.draft_section still technically accepts products (and sanitises them), but the prompt directs the
model to use update_product_profile for products; the acceptance-criterion behaviour lives in the
prompt + the two new tools.03_release/output/changelog.mdLive at apps/help/app/changelog/2026-07-10-product-conversation/page.mdx (goes live on merge).
Title: Onboarding now captures a detailed profile for every product you sell Personas: vendor PR: https://github.com/sustentus/sustentus/pull/630
When you set up your workspace, the assistant now builds a rich profile for each of your products — not just a name and description.
03_release/output/investor-update.mdWho it's for: Vendors onboarding to Sustentus What shipped: The onboarding assistant now builds a typed, confidence-scored profile for every product a vendor sells — from their website, then filling only the gaps. Why it matters: Richer product knowledge from day one advances Build the Bridge — Establish Product-Market Fit with Vendor Partners.
Every product is profiled, not just the first.
Dig deeper: https://github.com/sustentus/sustentus/pull/630 · https://help.sustentus.com/changelog/2026-07-10-product-conversation
03_release/output/release.md4852575)apps/docs/app/technical/packages/services/page.mdx — onboarding concierge section now describes the typed per-product profile + per-product gap interview and its read/update toolsservice-journey has no onboarding step, feature-role-matrix/products is a role-permission table, platform-overview is coarser-grain than thisapps/help/app/changelog/2026-07-10-product-conversation/page.mdx) + investor draft in this PR4852575)update_product_profile preserves reviewState while draft_section resets it to pending — accepted; per-item review is legacy (commit.ts) and launch confirms the blueprint as a whole.GAP_CONFIDENCE_THRESHOLD exported but used only within contract.ts — accepted (harmless; available for tests/future).analyse-source.ts extraction prompt + typed DraftBlueprintSchema.products.read_product_gaps + update_product_profile + prompt.ts loop.update_product_profile adds an unknown key.sanitiseSectionData("products", …) in the crawl (toCandidates) and in update_product_profile.prompt.ts override is products-only.