Kestrel Fastening Systems KESTREL FASTENING SYSTEMS

B2B

The B2B surfaces this baseline actually ships — account, CSR, quotes, reordering, kits, subscriptions — and which of them are inert without identities.

What the baseline guarantees

Layers: surface-swift 1.5.1 + feature-reordering + feature-subscription-orders +
feature-bom-configurator + feature-b2b-comms.
Account surfaces (surface-swift, replace/_content/Swift 2/Customer center/Account/):
  Addresses, Carts, Favorites, Orders, Quotes, Users
  plus Subscriptions (feature-subscription-orders)
CSR surfaces (Customer center/CSR/): Accounts, Carts, Orders, Users
Personal surfaces (Customer center/Customer center/): Change password, My addresses,
  My carts, My favorites, My orders, My profile > Edit profile, My quotes, My returns,
  My wallet
Overview (Customer center/Overview) — the dashboard shell. From Swift 2.4 this is a
  per-role tile dashboard on ONE shared page; the separate CSR tile dashboard was retired.
Quote flow: base ships EcomOrderFlow "Default Quote flow" and the order states "Price given",
"Approved", "Rejected". Shopping cart/Cart/Quote checkout user is the quote checkout page.
Reordering (feature-reordering 1.0.2, data-only, zero customCode):
  <site-root>/quick-order    — the pad; the form carries cartcmd=addmulti
  <site-root>/express-buy    — id="ExpressBuySearchForm"
  Both bind ProductsFrontend/Products.query (1.0.2 fixed this; it previously named a
  `Products` repository no install provisions, so every SKU read "Unknown SKU").
  Known cycle limitation: the 2-page Quick Order fragment does not fully re-bind after a
  deactivate/reactivate cycle (the page 404s). First activation is unaffected; a full
  re-deserialize restores it.
Kits (feature-bom-configurator 1.1.1): <site-root>/kit-configurator and its Detail page;
  PACK-BOM-0001 is ProductType 2 with two BOM slots.
Subscriptions (feature-subscription-orders 1.1.1): <site-root>/subscribe,
  <site-root>/account/subscriptions, PACK-SUB-PROD1, PAY2 (Invoice, resolves to
  DefaultCheckoutHandler, IRecurring), SHIP9 (Home delivery), and a ScheduledTask row
  "Place recurring orders" bound to the DW-native RecurringOrdersScheduledTask,
  ** TaskEnabled = false ** — the demo enables it to show renewals.
Dealer comms (feature-b2b-comms 1.0.2): 5 emails under /Newsletter Emails/Dealer Emails/
  (Cart reminder, Dealer welcome, How to order, Product compliance notice, Seasonal
  promotion) + EmailMarketingFlow 100500 with 3 steps.
NOT present by design: zero users. Every one of these pages renders its signed-out state
until you author identities. feature-rma's seeded return and feature-pricing's contract price
both key off customer number 98745621, which no user on this site holds.

Which skill covers it

dw-commerce-b2b (dynamo: true) — references/account-shape.md (the customer number as the
account key), references/dc-scoping.md (DC-as-user-group).
dw-demo-swift (dynamo: false) — references/customer-center.md (529 lines) for the
customer-center playbook, sign-in profiles, impersonation and checkout order fields.

Which MCP tools brand it

save_user_groups -> create_users -> assign_users_to_group / assign_groups_to_user
-> save_user_addresses
-> save_assortments -> assign_groups_to_assortment -> assign_products_to_assortment
   -> assign_shops_to_assortment -> assign_permissions_to_assortment
-> flag_assortments_for_rebuild -> build_assortments
-> check_assortment_product_access (the read-back)
Two ceilings to say out loud rather than work around:
- Impersonation (a CSR acting on behalf of an account) has NO MCP tool at all. Name the
  admin screen (Edit user > Impersonation tab) and stop.
- create_order_field errors on every call on this platform line: its MERGE into
  EcomOrderField passes an OrderFieldTypeID not present in EcomFieldType. A custom checkout
  field is an admin-screen operation.

Verify

MCP: get_user_groups returns the three base groups (Customers 1325, Account Admin 1270,
CSR 1292) and get_users_by_group_id on each returns 0 rows on a clean baseline. After
authoring: get_users_by_customer_number on your contract customer number returns exactly one
user, and every contact in an account returns the SAME customerNumber — a per-contact suffix
silently disables account-wide favourites, CSR account listing and contract pricing, because
all four features compare the string exactly (dw-commerce-b2b/references/account-shape.md:
42-45).
Storefront, BOTH auth states: GET <site-root>/customer-center signed out renders the anonymous
state; signed in as a Customers member it renders the account tiles. Assert both — the
signed-in assert alone cannot show the anonymous deny works.
Assortment: build_assortments first, then assert the item count is non-zero BEFORE switching
Active on. Activating a zero-item assortment does not add nothing; it takes the whole
catalogue away from everyone who holds it (dw-commerce-b2b/SKILL.md:117-119).
check_assortment_product_access must report no access for an out-of-scope user.