catalogue-demo-reseedrun.md00_intake/stub.mdThe chosen data strategy is to rebuild the catalogue through demo data (no migration of existing tenant data). Once the tiered links exist, the demo-data seeder still produces a flat, disconnected catalogue with skill categories — so a fresh demo does not show the new platform → product → service → skill hierarchy and the cascade has nothing coherent to filter.
Update the demo-data seeder to build a realistic tiered catalogue: platforms with products beneath
them, a shared generic service catalogue (onboarding, training, integration setup, …) linked
many-to-many to products, and skills linked many-to-many to services. Stop seeding skillCategory.
Seed demo leads along a valid product → service → skill path so the cascade and any dashboards read
coherent data.
skillCategory documents are created; seeded skills link to services.packages/services/src/db/services/demo-data/.01_define/output/spec.mdThe catalogue-hierarchy epic (pipeline/intake/catalogue-hierarchy/breakdown.md) rebuilds the flat
product/service/skill/skillCategory taxonomies into a tiered catalogue — platform → product →
service → skill — with many-to-many links on both lower edges. The agreed data strategy is to rebuild
the catalogue through demo data only (no migration of existing tenant data). The link foundations
(product-service-links, skill-service-retier) now exist, but the demo-data seeder still produces a
flat, disconnected catalogue with skillCategory — so a fresh demo does not show the new hierarchy and
the lead-capture cascade has nothing coherent to filter. This is stub 5 of 5 of the epic; without it a
fresh demo tenant does not demonstrate the tiered catalogue that Objective 2 of the okrs/2026-Q2 plan
(Build Repeatable Lead Generation Pipeline, KR: lead-to-vendor match accuracy) depends on.
Update the demo-data seeder so a fresh demo tenant renders the tiered catalogue coherently:
product-service-links link.skill-service-retier link. Stop seeding
skillCategory — skills hang off services instead.skillCategory documents; every seeded skill is linked to at least one
seeded service via the skill↔service link.(product, service, skill) combination that is valid under
the seeded links — the service is linked to the lead's product and the skill is linked to that
service.product-service-links / skill-service-retier /
product-services-admin) or the lead-capture cascade UI itself (stub 4, lead-capture-cascade).skills[]/products[].02_build/output/notes.mdpackages/services/src/db/services/demo-data/index.ts:CATALOGUE_PLATFORMS (platform → product
modules), UNIVERSAL_SERVICES / SPECIALIST_SERVICES (the shared generic service
catalogue), and CATALOGUE_SKILLS (generic skills, each mapped to one or more
services). Every service is referenced by at least one skill, so every valid path
resolves.populateDemoLeads now builds the tiered catalogue idempotently before seeding
leads: upserts services, skills (with their required services[] M2M), platforms and
products by (tenant, name), and links products↔services through the idempotent
productServiceLinkService.link. Universal services attach to every product;
specialist services rotate across products so each links to at least one product.
Skills carry their service links via the retired-category skill.services field — no
skillCategory is ever created.pickCataloguePath (pick a catalogue product → a service linked to it → skills linked
to that service), replacing the old independent random product/service/skills
picks. Industry stays a flat taxonomy reused from the tenant.DemoDataResult gains platforms/products/services/skills/catalogueLinks
counts (created-this-run; 0 on an idempotent re-run).apps/web/app/(app)/admin/demo-data/_components/demo-data-button.tsx: the "Last run
added" summary now surfaces the five new catalogue counts, so the Admin flow shows the
tiered catalogue it built.skill.services), all under
tenantId.skillCategory documents; seeded skills link to services — skillCategory was
retired by skill-service-retier; each seeded skill is created with a non-empty
services[].(product, service, skill) combination —
pickCataloguePath walks product → linked service → linked skills, so the service is
linked to the lead's product and the skills to that service.(tenant, name) and links go through the idempotent link(), so a re-run adds no
duplicate rows or links (catalogueLinks reports 0 on re-run).@sustentus/services is rebuilt in
CI before the web app typechecks, so the widened DemoDataResult is visible to the button.03_release/output/investor-update.mdWho it's for: Admin, and anyone running a Sustentus demo. What shipped: "Populate demo data" now builds a tiered platform → product → service → skill catalogue with linked records, and seeds demo leads along valid paths. Why it matters: Completes Build the Bridge / 2026-Q2 Objective 2 (Build Repeatable Lead Generation Pipeline) — demos now tell one coherent lead-generation story instead of flat, disconnected data.
One click produces a connected catalogue; skillCategory is fully retired.
Dig deeper: <merged-PR URL>
03_release/output/release.mdapps/docs/app/technical/demo-environment/page.mdx — "Populating Demo Leads" updated: the generator now builds the tiered platform → product → service → skill catalogue (idempotent, no skillCategory) and seeds leads along valid paths, replacing the stale "reuses existing reference data" lineplatform-overview/feature-role-matrix/service-journey already describe the tiered catalogue and cascade shipped by earlier stubs of this epic/code-review medium (standard complexity) run on the diff — one confirmed finding, fixed on branch:upsertNamed's findOne is filtered by
the softDeletePlugin pre-hook, but the catalogue unique indexes are plain, so a re-run after an
admin soft-deleted a seeded service/skill/product would collide and abort the whole seeder. Hardened
upsertNamed to catch the duplicate-key error and revive the archived row via the raw collection
(which bypasses the find hook), keeping the seeder idempotent. This also protects the downstream
link() call, since products/services are active again before linking.productServiceLinkService.link per-call model refetch is a few
extra queries but acceptable for a demo-only seeding path (not a hot path).skill.services), all under tenantId.skillCategory documents; seeded skills link to services — skills created with a non-empty services[]; skillCategory was retired by skill-service-retier.(product, service, skill) combination — pickCataloguePath walks product → linked service → linked skills.link(); catalogueLinks reports 0 on re-run.