Skip to Content

← All archived runs

Run: sdm-supply-taxonomy

run.md

Run: sdm-supply-taxonomy

  • branch: claude/laughing-planck-muh4ua
  • pr: #524

00_intake/stub.md

Stub: SDM dashboard — supply technology taxonomy

  • feature-slug: sdm-supply-taxonomy
  • epic: sdm-dashboard-data
  • personas: SDM
  • initiative: Build the Bridge / objective: Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: none
  • sequence: 1 of 8

Problem

Every row of the SDM dashboard keys on a platform (vendor in the mock — SAP, Oracle, Salesforce) and a module (product in the mock — MM, Fusion, CRM): see the goLiveDemand, quoteAndBidReadiness, goLiveRisks and expertStrength arrays in apps/web/lib/mock/sdm.ts. The real models can't express this: the product model is a bare { name } with no platform parent, the skill model hangs off a generic category, and lead.vendor is a reference to the paying Vendor org, not a technology family. With no platform→module taxonomy to group leads and experts by, no grid cell can be formed — demand, supply and coverage all aggregate over this key.

Proposed change

  • Introduce a two-level technology taxonomyplatform (SAP, Oracle, Salesforce) → module (MM, SD, FI, Fusion, HCM, CRM, CPQ…). Model it as the cleanest fit for the existing schema (e.g. a parent category/platform on the product model, or a dedicated taxonomy model) so a product resolves to (platform, module).
  • Tag the demand and supply sides with it: products/leads resolve to a cell platform+module, and an expert's productSkills (their skills) resolve to the same taxonomy, so demand and supply can be matched on a shared key.
  • Seed the taxonomy and back-fill the tags for the demo tenant (no admin CRUD UI this round).

Acceptance criteria (rough)

  • A product/skill resolves to a (platform, module) pair; the taxonomy is tenant-scoped and indexed.
  • A lead (its product/skills) can be bucketed into a platform+module cell by query.
  • An expert's product skills resolve to the same taxonomy, so demand and supply share one key.
  • The taxonomy and tags are seeded for the demo tenant so the grid can be built on real groupings.

Out of scope (this feature)

  • The demand/supply/coverage aggregation itself (features 3–6) — this only stands up the key they group on.
  • A taxonomy-management UI (admin CRUD, merge/rename) — seed + back-fill only this round.
  • Re-keying the lead.vendor org reference — the paying Vendor org is unchanged; this adds a separate technology dimension.

Notes for Define

  • Decide the model shape: extend product with a platform parent (+ reuse category for module), or a dedicated taxonomy model — pick the one that least duplicates category/skill. Confirm whether skill.category already encodes module and can be reused for the supply side.
  • Confirm the seed source for platform→module (SAP MM/SD/FI, Oracle Fusion/HCM, Salesforce CRM/CPQ — at minimum the modules present in the mock).
  • touches: packages/services/src/db/models/{product.ts,skill.ts,category.ts} (taxonomy fields + index), a seed/back-fill script for the demo tenant.

01_define/output/spec.md

Spec: SDM supply technology taxonomy (platform → module)

  • slug: sdm-supply-taxonomy
  • personas: SDM
  • touches: packages/services/src/db/models/{platform.ts (new), product.ts, user.ts, index.ts}, packages/services/src/db/migrations/, packages/services/src/db/seed/
  • complexity: standard

Problem

Every cell of the SDM "go live readiness" dashboard keys on a platform (SAP, Oracle, Salesforce) and a module (MM, SD, FI, Fusion, HCM, CRM, CPQ) — the mock encodes these as the vendor and product fields in apps/web/lib/mock/sdm.ts across the demand, bid-readiness, risk and expert-strength sections. The real models can't express this two-level key: product is a flat thin taxonomy with no platform parent, and there is no platform entity at all (lead.vendor is the paying Vendor org, not a technology family). Without a platform→module taxonomy to group leads and experts by, no grid cell can be formed — every downstream SDM feature (demand aggregation, supply coverage, coverage-risk, expert strength) aggregates over this key. This is feature 1 of the sdm-dashboard-data epic and the prerequisite for all of it; it advances Build the Bridge / Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow by standing up the real per-tenant supply key the SDM cockpit groups on.

Proposed change

Introduce a two-level technology taxonomy in @sustentus/services — a new tenant-scoped platform entity that product (the module) hangs off — so demand and supply resolve to the same (platform, module) key. No dashboard UI and no admin CRUD this round: model + migration + seed only.

Schema / model changes

  1. New platform collection. Add packages/services/src/db/models/platform.ts built on the shared createTaxonomySchema (the same factory service/product/industry use): tenantId, normalised name (lowercase/trim/length), isActive default true, soft-delete + tenant plugins, {tenantId, name} unique index. Add getPlatformModel, export it from models/index.ts, and load it where product is referenced (so the product → platform populate resolves).
  2. product becomes the module. Add a platform: ObjectId ref (to platform) on the product model. Because product reuses the shared ITaxonomy type, extend the product type/schema locally rather than widening the shared taxonomy. Change the product uniqueness from {tenantId, name} to {tenantId, platform, name} so the same module name can exist under different platforms, and add an index on {tenantId, platform} for cell grouping.
  3. Re-point the SDM area to the platform key. Change sdm.area.platforms (today ref: "product", a documented placeholder) to ref: "platform", and update the ISDMArea comment/type accordingly. The {tenantId, "area.platforms"} index is unchanged in shape.

Demand & supply resolution (no per-row re-tagging)

  1. Demand (lead.product) and supply (expert.products) already reference product, so once a product carries its platform parent, both sides resolve to (platform, module) transitively — no new fields on lead or expert, and no per-lead/per-expert re-tagging. This run only guarantees that demo leads/experts reference products that have a platform set.

Seed + back-fill (versioned, tenant-aware, idempotent — ts-migrate-mongoose)

  1. Seed the platform→module taxonomy for the demo tenant from the mock's set, per tenant: SAP → MM, SD, FI · Oracle → Fusion, HCM · Salesforce → CRM, CPQ. Create the platform rows, ensure the module product rows exist, and set each module's platform parent.
  2. Back-fill product.platform for existing module rows by mapping known module names to their platform (per the seed map); products that don't map to a known platform are left unset and reported (they form no cell). Back-fill sdm.area.platforms by replacing each existing product id with its product's platform parent id (de-duplicated).
  3. Ensure demo leads/experts in the demo tenant reference products that now have a platform, so the grid can be built on real groupings (back-fill/assign demo lead.product / expert.products only where missing — do not invent demand/supply beyond what the demo needs to form cells).
  4. The migration is idempotent (a second up is a no-op) and follows the established one-way/ symmetric down convention in migrations/1782000000000-taxonomy-normalization.ts; existing rows' isActive is left untouched.

Acceptance criteria

  • A platform collection exists (built on the shared taxonomy factory), is tenant-scoped, name- normalised, has a {tenantId, name} unique index, and is exported from models/index.ts with a getPlatformModel.
  • product carries a platform ref to platform; a product (module) resolves to a (platform, module) pair, product uniqueness is {tenantId, platform, name}, and `{tenantId,

platform}` is indexed.

  • A lead can be bucketed into a platform + module cell by query (via lead.product → platform), and an expert's products resolve to the same key — demand and supply share one taxonomy.
  • sdm.area.platforms references the platform collection (not product), and existing area assignments are back-filled to the corresponding platform ids.
  • A versioned migration seeds the platform→module taxonomy and back-fills product.platform, sdm.area.platforms, and demo lead/expert product tags per tenant for the demo tenant; it is idempotent (a second up makes no further changes) and leaves existing isActive untouched.
  • pnpm typecheck, pnpm lint, and pnpm build pass; no code treats sdm.area.platforms as a product ref.

Out of scope

  • The demand / supply / coverage aggregation and any dashboard wiring (epic features 3–6) — this run only stands up the key they group on; no apps/web or apps/dashboards changes.
  • A taxonomy-management UI (admin CRUD, merge/rename of platforms/modules) — seed + back-fill only.
  • Re-keying lead.vendor — the paying Vendor org reference is unchanged; this adds a separate technology dimension.
  • The skill / skillCategory axis — module is product, not skillCategory; "skill level" in the grid is expert seniority/proficiency, resolved by later features, not part of this taxonomy.
  • Merging platform/product into a single polymorphic collection — kept separate, consistent with the taxonomy-normalization decision.

Open questions

  • none — model shape (dedicated platform collection + product.platform ref) and the sdm.area.platforms re-point + back-fill were both confirmed during Define.

02_build/output/notes.md

Build notes: sdm-supply-taxonomy

  • commits: feat(sdm-supply-taxonomy): platform→module supply taxonomy (model + migration)

What changed

  • packages/services/src/db/models/platform.ts (new): the platform parent collection (SAP, Oracle, Salesforce), built on the shared createTaxonomySchema like service/industry — tenant-scoped, name-normalised, {tenantId, name} unique. Exposes getPlatformModel.
  • packages/services/src/db/models/index.ts: export the new platform model.
  • packages/services/src/db/models/product.ts: product is now the module. Replaced the factory-built schema with a local one (extending the taxonomy shape rather than widening the shared ITaxonomy) that adds a platform ref. Uniqueness moved from {tenantId, name} to {tenantId, platform, name}, plus a {tenantId, platform} index for cell grouping. IProduct is now ITaxonomy & { platform?: ObjectId | null } — still satisfies TaxonomyService<T extends ITaxonomy>, and admin CRUD (platform defaults to null) still enforces a unique name per tenant.
  • packages/services/src/db/models/user.ts: re-pointed sdm.area.platforms from ref: "product" to ref: "platform" and updated the ISDMArea doc comment (the placeholder note is now resolved).
  • packages/services/src/db/migrations/1782700000000-sdm-supply-taxonomy.ts (new): per-tenant, idempotent migration that seeds the platform→module taxonomy, sets/back-fills product.platform, re-points sdm.area.platforms (product id → its platform parent, keeping ids that already point at a platform so a re-run is a no-op), tags synthetic demo+ experts/leads with platform-bearing products so the grid forms cells, and swaps the products unique index. down is a documented no-op per the taxonomy-normalization convention.

Acceptance criteria status

  • A platform collection exists (shared taxonomy factory), tenant-scoped, name-normalised, {tenantId, name} unique, exported with getPlatformModel.
  • product carries a platform ref; resolves to (platform, module); uniqueness is {tenantId, platform, name} with {tenantId, platform} indexed.
  • A lead buckets into a cell via lead.product → platform, and expert.products resolve to the same key — both reference product, which now carries platform, so the cell is transitive.
  • sdm.area.platforms references the platform collection; existing assignments are back-filled to platform ids by the migration.
  • A versioned migration seeds the taxonomy and back-fills product.platform, sdm.area.platforms and demo lead/expert product tags per tenant; idempotent; leaves isActive untouched.
  • pnpm typecheck, pnpm lint, pnpm build pass — verified by CI on this PR (no code treats area.platforms as a product ref; checked apps/web + services usages).

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.

Notes for review

  • Index swap is handled in the migration, not just the schema: the old tenantId_1_name_1 unique index on products is dropped and {tenantId, platform, name} (unique) + {tenantId, platform} created, since Mongoose autoIndex only ever adds, never drops.
  • Demo data back-fill is tightly scoped to synthetic demo+ users (experts by email, leads via their demo+ vendor) and only fills a missing product link — real tenant data is never touched.
  • Follow-up (out of scope here): the demo-data generator (services/demo-data/index.ts) still creates experts without products; a later change should have it assign platform-bearing products so freshly generated demo batches keep a populated supply side. This migration fixes existing data only.

03_release/output/investor-update.md

Supply taxonomy: the data key behind the SDM cockpit

Who it's for: Service delivery managers — our internal supply-readiness role. What shipped: A tenant-scoped platform → module technology taxonomy (e.g. SAP → MM) now keys every lead and expert in the data layer. Why it matters: It is the foundation the SDM go-live readiness dashboard groups on — advancing Build the Bridge, objective "Validate Technical Infrastructure & Payout Flow".

Dig deeper: <merged-PR URL> · <changelog entry URL>

03_release/output/release.md

Release: sdm-supply-taxonomy

  • pr: #524 · merged: <pending — on Ready-to-merge tick>
  • CI: green on build commit (Format · Lint · Typecheck · Migrate preview database · Audit database all ✓)
  • technical docs: no technical docs impact — adds a model + migration; the database lifecycle page documents migration/seeding conventions generically (not per-collection) and they still hold
  • business docs: no business docs impact — no user-facing behaviour change; the SDM dashboard still reads mock data (aggregation/wiring is epic features 3–6, out of scope)
  • release notes: investor-only — no end-user note (internal data-layer change, no UI)
  • deploy: <pending>
  • sent: <pending>

Review summary

  • Regression caught & fixed on branch (high): db/services/sdm-area/index.ts (shipped by sdm-area-foundation) filtered lead.product / expert.products directly against area.platforms. Re-pointing area.platforms to platform ids made that $in compare platform ids to product ids → empty "my area" scope, and it violated AC6 ("no code treats sdm.area.platforms as a product ref"). Fixed: the service now resolves owned platform ids to the module (product) ids under them and filters by those (transitive resolution the spec intended).
  • Migration correctness sweep (collation/unique-index conflict, soft-deleted dup keys, case-sensitive matching, same-name-across-platforms): reviewed and refuted — the prior {tenantId, name} unique index was already non-collated and non-partial (so same-name dups, incl. soft-deleted, were already impossible), names are lowercase on write, and each seed module maps to exactly one platform. The Migrate preview database CI check passed on this migration.
  • area.platforms re-point drops product ids that map to no platform: accepted — area.platforms was a documented placeholder this feature owns converting; only demo data has an area assigned.
  • Follow-up (unchanged, out of scope): the demo-data generator still creates experts without products — noted in build notes for a later change.

Acceptance check (vs spec)

  • platform collection on the shared taxonomy factory, tenant-scoped, name-normalised, {tenantId, name} unique, exported with getPlatformModelmodels/platform.ts + models/index.ts.
  • product carries a platform ref; resolves to (platform, module); uniqueness {tenantId, platform, name} + {tenantId, platform} index — models/product.ts (and migration index swap).
  • Lead buckets via lead.product → platform; expert.products resolve to the same key — both ref product, which now carries platform (transitive).
  • sdm.area.platforms refs platform; existing assignments back-filled — models/user.ts + migration step 3.
  • Versioned migration seeds the taxonomy and back-fills product.platform, area assignments and demo lead/expert tags per tenant; idempotent; isActive untouched — migrations/1782700000000-sdm-supply-taxonomy.ts.
  • typecheck / lint / build pass; no code treats area.platforms as a product ref — green CI on the build commit.