blueprint-commit-servicerun.md00_intake/stub.mdLaunching a verified blueprint means creating records across 6+ existing services with referential links (services reference skills and categories; products reference categories) — issue #588 Q4. Done naively, a mid-flight failure leaves a half-configured tenant that is neither empty nor set up, which breaks the epic's "done, verified and completed" contract.
A commit service that takes a tenant's verified blueprint and materialises it into the existing services in dependency order (categories → skills → products → services → SLA tiers → settings/profile), as close to atomic as the store allows (Mongo transaction if available, otherwise ordered writes with compensating rollback and a resumable/retryable commit record). Only approved/edited items commit; rejected items are skipped. Provenance and confidence are stamped onto created records per the model decisions in stub 1. On success the blueprint becomes committed and the tenant's onboarding state advances; on failure the tenant is left observably un-launched with a reported reason.
Decide transaction vs best-effort+repair explicitly against the current Mongo deployment (replica-set transactions may not be available everywhere — check db/connection.ts). Reuse the existing per-entity services rather than writing to models directly, so validation/workflow rules apply. touches: packages/services/src/db/services/ (new onboarding/ or similar), existing product / service / skill / sla / tenant-setting services.
01_define/output/spec.mdLaunching a verified tenant blueprint means creating records across the existing supply taxonomy and
tenant configuration with referential links intact — issue #588 Q4. Done naively, a mid-flight failure
leaves a half-configured tenant that is neither empty nor set up, which breaks the epic's "done,
verified and completed" contract. This is stub 3 of 7 in the tenant-onboarding-wizard epic
(depends-on: onboarding-blueprint-model, already specced): the persisted blueprint document and its
draft → in_review → verified → committed lifecycle exist, but nothing yet materialises a verified
blueprint into the real services. It advances Build the Bridge / Q2-2026 O1 — Establish PMF with
Vendor Partners (KR: onboard 8+ vendors onto paid tiers): a tenant that can't be reliably launched
can't be onboarded.
A tenant-scoped commit service that materialises a blueprint in the verified state into the
existing supply taxonomy and tenant configuration inside a single Mongo transaction, then advances
the blueprint to committed and the tenant's onboarding state to completed.
Domain model (authoritative — product-led, three levels).
product-service-link join records this.There is no category or platform level (the earlier "categories → platform" mapping is rejected).
The existing schema already matches this model — product-service-link is the M:N join and
skill.services holds the (single, here) parent service — so commit reuses the services as-is; no
schema change to the taxonomy.
Commit order (referential): products → services (+ link each to its products) → skills (each attached to its one service) → sla → settings → profile. Products first (nothing depends on a service
or skill pre-existing); services after products so the M:N links resolve; skills after services so each
skill's one service exists.
Commit input contract (what stub 5 drafts and this stub reads — blueprint items already carry
key, label, data, confidence, provenance, reviewState):
name from label, optional description from data).data.productKeys: string[] names the product items it belongs
to, and commit links the created service to each resolved product.data.serviceKey: string names its one parent service item.data).key / category / value from data).Cross-references resolve by blueprint item key to the id created earlier in the same commit; a
reference to a rejected or absent item is a commit error (the transaction aborts).
What commits. Only items whose reviewState is approved or edited are created; pending and
rejected items are skipped. edited items commit with their edited data / label.
Provenance & confidence (decision A). The real taxonomy/config collections are left unchanged
(no new fields, kept lean). Each blueprint item already carries provenance and confidence; the
commit writes a commit record on the blueprint that links every committed item (by section + key)
to the id and collection of the record it produced, plus the outcome (committedAt, and on failure a
failureReason). Each created record is therefore traceable back to its originating item's provenance
and confidence via that link.
Atomicity — Mongo transaction (primary path). The deployment supports replica-set transactions
(invoice and proposal/expert-bid-proposal-flow already use mongoose.startSession() +
session.withTransaction()), so all writes — taxonomy records, links, sla/settings/profile, the
blueprint/tenant state changes, and the commit record — run in one transaction: everything lands
together or the transaction aborts and nothing is written. Reaching atomicity this way means the reused
per-entity create/link/upsert methods are threaded with an optional session so their writes
join the transaction (reuse the services so validation rules apply, per the stub). The
best-effort + compensating-rollback + resumable-commit engine is not built (superseded by the
transaction path).
Outcome. Success: blueprint → committed, tenant onboarding state → completed, records + links +
commit record written. Failure: the transaction aborts, nothing persists, the blueprint stays
verified, the tenant stays un-completed, and the failureReason is readable so the caller can
report why launch did not happen and retry.
verified blueprint creates, for that tenant only, the correct product / service /
skill / sla / settings / profile records with links intact: each service linked to its products
(many-to-many), each skill attached to its one service.committed and the tenant onboarding state advances to
completed; both are readable afterwards.approved and edited items are created; pending and rejected items are not, and
edited items commit with their edited values.key) to the id and
collection of the record it produced, so each created record is traceable to its originating
item's provenance and confidence.verified, the tenant stays un-completed, and the failureReason is readable — verified by
re-reading after the failed call.verified state is rejected with a clear error and writes
nothing.categories section on the blueprint, if present, creates no records (product is the top-level
entity; there is no category/platform collection).verified.concierge-wizard-ui).data payload sub-shapes, and how profile
maps onto individual tenant fields — none change the observable outcomes above.02_build/output/notes.mdfeat: blueprint-commit-service — atomic transactional commit of a verified blueprintpackages/services/src/db/models/onboarding-blueprint.ts — added the commitRecord field
(BlueprintCommitRecord + BlueprintCommitItem types and embedded schemas). This is decision A:
provenance/confidence stay on the blueprint items, and the commit record links each committed item
(by section + key) to the id + collection of the record it produced. No fields added to the real
taxonomy/config collections. Optional embedded subdoc → no migration needed (absent on existing docs).packages/services/src/db/services/onboarding-blueprint/commit.ts (new) — commitVerifiedBlueprint:
the transactional commit. Reads the tenant's blueprint; a committed one is an idempotent no-op, a
non-verified one throws. Otherwise, inside one session.withTransaction, it creates records in
product-led order — products → services (+ link each to its products via product-service-link) →
skills (each attached to its one service) → sla → settings → profile — resolving cross-references by
blueprint item key, then advances the blueprint to committed (compare-and-set guarded on
verified) and the tenant onboarding state to completed. On any failure the transaction aborts
(nothing persists) and a failed commit record with the reason is written out-of-band so it is
readable on re-read.onboarding-blueprint/index.ts — commitBlueprint(tenantId) method on the service + re-export of
BlueprintCommitResult. db/services/index.ts — surfaced BlueprintCommitResult on the barrel.session? added to taxonomy/base.ts create (products + services), skill create +
validateServiceIds, product-service-link link, sla upsertSlaDefinition, tenant-setting create,
tenant setOnboardingStatus. All params are optional and defaulted — existing callers are unchanged.Blueprint items already carry key/label/data/confidence/provenance/reviewState. Per section:
product → name/description; service → name + data.productKeys: string[] (parent product item keys);
skill → name + data.serviceKey: string; sla → data.{stage,ownerRole,slaType,targetDays,warningDays,breachDays,impact,escalationEnabled};
settings → data.{key,category,value}; profile → data.name (+ blob stored under tenant.settings.profile).
A categories section is never read (no target collection — product is the top level).
committed and tenant → completed, both readable.approved/edited items commit (isCommittable filter); edited items commit their stored (edited) data/label.key) → produced record id + collection.verified, tenant un-completed, failureReason readable.committed blueprint is a no-op (status guard); no duplicate records/links.verified blueprint rejected with a clear error, writes nothing.categories section creates no records.tenantId; link re-validates product/service belong to the tenant).@sustentus/services with no route or UI
surface until stub 6 (concierge-wizard-ui wires the launch button), and the repo has no test
infrastructure (per CONVENTIONS.md). The criteria above are implemented-to-spec; the first end-to-end
exercise lands with stub 6 (or a throwaway script against a replica-set Mongo).invoice and proposal). On a
non-transactional deployment withTransaction would throw and every commit would report failure — the
spec explicitly decided the transaction path over a best-effort engine.tenant.name when provided and stores the profile payload under tenant.settings.profile.<key>.skill schema still allows multiple services; the commit always sets exactly one, per the
product→service→skill model. Adapting the settings-level UI forms/pages is separate work (out of scope).