Skip to Content

← All archived runs

Run: catalogue-demo-reseed

run.md

Run: catalogue-demo-reseed

  • branch: claude/pipeline-catalogue-demo-reseed-ulswxi
  • pr: #601

00_intake/stub.md

Stub: Reseed demo data for the tiered catalogue

  • feature-slug: catalogue-demo-reseed
  • epic: catalogue-hierarchy
  • personas: Admin
  • initiative: Build the bridge / objective: okrs/2026-Q2 Objective 2 — Build Repeatable Lead Generation Pipeline (lead-to-vendor match accuracy)
  • depends-on: product-service-links, skill-service-retier
  • sequence: 5 of 5

Problem

The 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.

Proposed change

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.

Acceptance criteria (rough)

  • Running the demo-data seeder produces platforms → products → services → skills with populated M2M links, tenant-scoped.
  • No skillCategory documents are created; seeded skills link to services.
  • Seeded demo leads reference a product/service/skill combination that is valid under the links.
  • The seeder is idempotent/re-runnable as before and the "Populate demo data" admin flow still works.

Out of scope (this feature)

  • Migrating or reconciling real historical tenant data or pre-existing leads.
  • Any model or admin change (stubs 1–3) or the cascade itself (stub 4).

Notes for Define

  • Sequenced last so the seed reflects the final shape (links + retired categories + cascade).
  • Follow the dashboards mock-data / demo-data conventions; keep volumes realistic per the existing seeder.
  • touches: packages/services/src/db/services/demo-data/.

01_define/output/spec.md

Spec: Reseed demo data for the tiered catalogue

  • slug: catalogue-demo-reseed
  • personas: Admin
  • touches: packages/services/src/db/services/demo-data/
  • complexity: standard

Problem

The 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.

Proposed change

Update the demo-data seeder so a fresh demo tenant renders the tiered catalogue coherently:

  • Seed platforms with products beneath them (platform → product stays as-is on top).
  • Seed a shared generic service catalogue (onboarding, training, integration setup, …) and link services many-to-many to products, tenant-scoped, using the product-service-links link.
  • Seed skills linked many-to-many to services using the skill-service-retier link. Stop seeding skillCategory — skills hang off services instead.
  • Seed demo leads along a valid product → service → skill path (the service is linked to the lead's product, and the skill is linked to that service) so the cascade and any consuming dashboards read coherent data.
  • Keep the seeder re-runnable and keep the existing "Populate demo data" admin flow working, at realistic volumes consistent with the current seeder.

Acceptance criteria

  • Running the demo-data seeder (the "Populate demo data" admin flow) produces a tenant-scoped catalogue of platforms → products → services → skills, with populated product↔service and service↔skill many-to-many links.
  • The seeder creates no skillCategory documents; every seeded skill is linked to at least one seeded service via the skill↔service link.
  • Every seeded service is linked to at least one seeded product via the product↔service link (no orphan services in the demo catalogue).
  • Every seeded demo lead references a (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.
  • The seeder remains re-runnable as before: the "Populate demo data" admin flow completes without error on a fresh run and on a re-run, and does not create duplicate catalogue links.
  • Existing seeded lead-graph output (proposals, quotes, milestones, invoices, matches, status history, activities and the delivery-side records) still populates; build, lint, and typecheck stay green.

Out of scope

  • Migrating, backfilling, or reconciling real historical tenant catalogue data or pre-existing leads — historical leads keep their existing flat product/service/skill values.
  • Any model or admin change (epic stubs 1–3, product-service-links / skill-service-retier / product-services-admin) or the lead-capture cascade UI itself (stub 4, lead-capture-cascade).
  • Expert profiles and the matching engine — experts keep their flat skills[]/products[].
  • Platform-tier changes and the SDM demand/coverage grids — platform stays as-is.

Open questions

  • none

02_build/output/notes.md

Build notes: catalogue-demo-reseed

  • commits: feat: catalogue-demo-reseed — seed a tiered platform→product→service→skill demo catalogue

What changed

  • packages/services/src/db/services/demo-data/index.ts:
    • New module-level catalogue seed data — 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.
    • Leads are now seeded along a valid product → service → skill path via 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.

Acceptance criteria status

  • Running the seeder produces platforms → products → services → skills with populated M2M links, tenant-scoped — the catalogue builder creates all four tiers and links products↔services (join model) and skills↔services (skill.services), all under tenantId.
  • No skillCategory documents; seeded skills link to services — skillCategory was retired by skill-service-retier; each seeded skill is created with a non-empty services[].
  • Every seeded service is linked to at least one product — universal services attach to every product; specialist services are distributed by rotation so each is covered.
  • Every seeded demo lead references a valid (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.
  • The seeder remains re-runnable without duplicate catalogue links — entities upsert by (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).
  • Existing seeded lead-graph output still populates — only the catalogue/path selection changed; the proposal/quote/milestone/invoice/match/blocker/etc. graph is untouched.

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. @sustentus/services is rebuilt in CI before the web app typechecks, so the widened DemoDataResult is visible to the button.

Notes for review

  • The catalogue is seeded by the demo-data path, per the epic's data strategy (no migration of existing tenant data). Historical/flat leads are untouched.
  • Product→service linking is deterministic (universal + rotating specialist) rather than random, so re-runs produce a stable link set — this is what keeps re-runs duplicate-free and coverage guaranteed.
  • Volumes: 4 platforms, 13 product modules, 8 services, 10 skills — realistic and small, consistent with the existing seeder's demo scale.

03_release/output/investor-update.md

Demos now showcase the full tiered catalogue end to end

Who 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.md

Release: catalogue-demo-reseed

  • pr: #601 (https://github.com/sustentus/sustentus/pull/601) · merged: pending (gated on the Ready-to-merge tick + CI)
  • CI: pending check at merge time (see step 6)
  • technical docs: apps/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" line
  • business docs: no business docs impact — this changes demo-seed content only, not real product behaviour; platform-overview/feature-role-matrix/service-journey already describe the tiered catalogue and cascade shipped by earlier stubs of this epic
  • release notes: investor-only — internal/demo-tooling change (the "Populate demo data" admin flow); framed as demo quality / epic completion toward Objective 2. No end-user changelog — nothing a customer does differently in the product today
  • deploy: pending (step 7, after merge)
  • sent: pending (step 8, gated on green deploy)

Review summary

  • /code-review medium (standard complexity) run on the diff — one confirmed finding, fixed on branch:
    • Soft-deleted catalogue row → E11000 on re-run (fixed). 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.
  • No other actionable findings. The productServiceLinkService.link per-call model refetch is a few extra queries but acceptable for a demo-only seeding path (not a hot path).

Acceptance check (vs spec)

  • Seeder produces platforms → products → services → skills with populated M2M links, tenant-scoped — catalogue builder creates all four tiers, links products↔services (join model) and skills↔services (skill.services), all under tenantId.
  • No skillCategory documents; seeded skills link to services — skills created with a non-empty services[]; skillCategory was retired by skill-service-retier.
  • Every seeded service is linked to at least one product — universal services attach to every product; specialist services rotate across products so each is covered.
  • Every seeded demo lead references a valid (product, service, skill) combination — pickCataloguePath walks product → linked service → linked skills.
  • Seeder remains re-runnable without duplicate catalogue links — upsert-by-name + idempotent link(); catalogueLinks reports 0 on re-run.
  • Existing seeded lead-graph output still populates — only the catalogue/path selection changed; the rest of the lead graph is untouched.