Skip to Content

← All archived runs

Run: blueprint-commit-service

run.md

Run: blueprint-commit-service

  • branch: claude/pipeline-blueprint-commit-548sju
  • pr: #606

00_intake/stub.md

Stub: Atomic blueprint commit service

  • feature-slug: blueprint-commit-service
  • epic: tenant-onboarding-wizard
  • personas: Admin, Vendor
  • initiative: Build the Bridge / objective: Q2-2026 O1 — Establish PMF with Vendor Partners
  • depends-on: onboarding-blueprint-model
  • sequence: 3 of 7

Problem

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

Proposed change

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.

Acceptance criteria (rough)

  • Committing a verified blueprint creates the correct linked records in all touched services for that tenant only, and marks blueprint + tenant state accordingly.
  • A mid-commit failure never leaves the tenant half-launched: either rolled back or resumable to completion, with the state readable afterwards.
  • Rejected blueprint items are not created; approved-with-edits items commit with their edited values.
  • Committing the same blueprint twice is a no-op (idempotent).

Out of scope (this feature)

  • Generating or reviewing blueprint content (stubs 4–6).
  • Any UI — the launch button wires up in stub 6.
  • Post-commit editing (existing CRUD).

Notes for Define

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

Spec: Atomic blueprint commit service

  • slug: blueprint-commit-service
  • personas: Admin, Vendor
  • touches: packages/services/src/db/services/onboarding-blueprint/ (new commit method + commit record), packages/services/src/db/models/onboarding-blueprint.ts (commit-record field), packages/services/src/db/services/{product,service,skill,product-service-link,sla,tenant-setting,tenant}/ (session-threaded reuse), packages/services/src/db/connection.ts (transaction session)
  • complexity: complex

Problem

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

Proposed change

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 is the top-level entity — the datapoint a lead attaches to. Created via the product service.
  • Service derives from products, many-to-many: one service can belong to many products, and one product can have many services. The product-service-link join records this.
  • Skill derives from services: a skill belongs to one service; a service can have many skills. Created via the skill service with that single service id.

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):

  • product item → a product record (name from label, optional description from data).
  • service item → a service record; data.productKeys: string[] names the product items it belongs to, and commit links the created service to each resolved product.
  • skill item → a skill record; data.serviceKey: string names its one parent service item.
  • sla item → an sla-definition stage upsert (stage + timers from data).
  • settings item → a tenant-setting row (key / category / value from data).
  • profile item → the tenant's company-profile fields.

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.

Acceptance criteria

  • Committing a 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.
  • On success the blueprint advances to committed and the tenant onboarding state advances to completed; both are readable afterwards.
  • Only approved and edited items are created; pending and rejected items are not, and edited items commit with their edited values.
  • The blueprint's commit record links every committed item (by section + 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.
  • A mid-commit failure (e.g. a service write throws, or a cross-reference cannot resolve) leaves the tenant not half-launched: no records or links from that commit persist, the blueprint stays verified, the tenant stays un-completed, and the failureReason is readable — verified by re-reading after the failed call.
  • Committing the same blueprint twice is a no-op: the second call creates no additional records or links and leaves state consistent (idempotent).
  • Committing a blueprint that is not in verified state is rejected with a clear error and writes nothing.
  • A categories section on the blueprint, if present, creates no records (product is the top-level entity; there is no category/platform collection).
  • Every commit path is tenant-scoped — a commit for tenant A never reads or writes tenant B's blueprint, records or links.

Out of scope

  • A category / platform taxonomy level — rejected; product is the top-level entity.
  • Changing the skill schema's service cardinality: the schema keeps its existing shape and the commit simply sets exactly one service per created skill. Adapting the settings-level UI forms/pages to reflect product → service → skill is separate work (settings UI / stub 6), tracked outside this run, not done here.
  • Generating, drafting, reviewing or verifying blueprint content (stubs 4–6), and moving a blueprint into verified.
  • Any UI — the launch button and its wiring land in stub 6 (concierge-wizard-ui).
  • A best-effort + compensating-rollback + resumable/retryable commit-record engine — superseded by the transaction path.
  • Post-commit editing of the created records (existing per-entity CRUD already covers this).

Open questions

  • none. The provenance/confidence home (decision A — commit record on the blueprint) and the taxonomy shape (product-led product → service → skill, no categories) are settled above. Build details left open — the exact commit-record sub-schema, per-section data payload sub-shapes, and how profile maps onto individual tenant fields — none change the observable outcomes above.

02_build/output/notes.md

Build notes: blueprint-commit-service

  • commits: feat: blueprint-commit-service — atomic transactional commit of a verified blueprint

What changed

  • packages/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.tscommitBlueprint(tenantId) method on the service + re-export of BlueprintCommitResult. db/services/index.ts — surfaced BlueprintCommitResult on the barrel.
  • Session threading (so reused services' writes join the transaction, keeping their validation): optional 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.

Commit input contract (for stub 5, the concierge agent, to produce)

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; sladata.{stage,ownerRole,slaType,targetDays,warningDays,breachDays,impact,escalationEnabled}; settingsdata.{key,category,value}; profiledata.name (+ blob stored under tenant.settings.profile). A categories section is never read (no target collection — product is the top level).

Acceptance criteria status

  • Correct linked records per tenant (service↔products M:N, skill→one service) — commit order + key resolution.
  • Success advances blueprint → committed and tenant → completed, both readable.
  • Only approved/edited items commit (isCommittable filter); edited items commit their stored (edited) data/label.
  • Commit record links every committed item (section + key) → produced record id + collection.
  • Mid-commit failure: transaction aborts (no records/links), blueprint stays verified, tenant un-completed, failureReason readable.
  • Idempotent: an already-committed blueprint is a no-op (status guard); no duplicate records/links.
  • Non-verified blueprint rejected with a clear error, writes nothing.
  • A categories section creates no records.
  • Every path tenant-scoped (all service calls take tenantId; link re-validates product/service belong to the tenant).

Verify result

  • Mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here.
  • No runtime exercise yet: this is a service-layer method in @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).

Notes for review

  • Atomicity relies on replica-set transactions (confirmed in use by 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.
  • Profile has no per-entity service, so it writes the tenant document directly (in-session): sets tenant.name when provided and stores the profile payload under tenant.settings.profile.<key>.
  • The 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).