24. Starter Kit Apply E2E Verification — Package Behavior Matrix (2026-07-20)
24.1 Purpose
A verification-only pass (no code changed in either repo) confirming the Tenant Starter Kit Preview/Apply flow end-to-end against the current four Shumoul subscription packages (see Subscription Packages & Entitlements), extending Chapter 22's single-tenant live pass (which used one legacy test tenant entitled for everything, and so never exercised cross-package Blocked/Skipped differentiation).
24.2 Current flow — confirmed correct, no changes needed
TenantStarterKitService (Shumoul.Framework.MultiTenancy.Api/StarterKits/) orchestrates seven steps —
AccountingChartAccounts → AccountingChartMappings → TenantOperationalDefaults → CashBankAccountingLink → InventoryAccountCategory → CostCenters (accounting chain) and ProductCatalogPrerequisites → ProductCatalogCategories → ProductCatalogItems (product chain) — by delegating to each step's own,
independently-tested ITenant*ApplyService. The orchestrator itself never writes a tenant database
directly.
- Entitlement chain confirmed correct end-to-end:
IsFeatureAllowedAsync→GetTenantAllowedFeatureKeysAsync→ITenantFeatureService.TenantFeaturesIDs→ITenantSubscriptionService.GetTenantPackageIdresolves viaTenantSubscription.PlanPackageFK.PackageId(i.e.TenantSubscription.PackageId → SubscriptionPlanPackage.Id → SubscriptionPlanPackage.PackageId → SubscriptionPackage.Id), filtered toStatus == Active— the correct chain, not the incorrect direct join a much earlier phase mistakenly used elsewhere. No fix was needed here. - Onboarding never auto-triggers Starter Kit: confirmed by exhaustive source search — zero references
to "StarterKit" exist anywhere in
Shumoul.Saas.Api'sShumoul.Infrastructure/Shumoul.Application/Shumoul.Apiprojects. Onboarding's settings-patch apply and Starter Kit remain two entirely separate administrative surfaces, as required. needsCostCentersis read from the tenant's latest onboarding answer viaOnboardingAnswerReader, with an explicitIncludeCostCentersrequest value always overriding the onboarding-derived default — confirmed both by source and live call (§24.4).- Idempotency mechanism: every one of the seven underlying BackEnd apply services
(
AccountingChartTemplateAccountApplyService,AccountingChartTemplateMappingApplyService,InventoryAccountCategoryTemplateApplyService,CostCenterTemplateApplyService,ProductCatalogPrerequisiteTemplateApplyService,ProductCatalogTemplateApplyService,ProductCatalogItemTemplateApplyService) already has its own dedicatedReApplyingTheSameSnapshotIsIdempotent-style unit test, andTenantOperationalDefaultsProvisioningServicehasSkipExisting_AndCreateNoDuplicates_OnSecondApply— this was already thoroughly proven at the per-service level before this pass; this pass adds the missing end-to-end confirmation (§24.4). - Error model already correctly distinguishes
Blocked(package doesn't allow it) /Skipped(excluded by request, or a dependency didn't succeed) /Failed(attempted, didn't succeed) /Applied/Succeeded/Planned(worked) — every message observed live was specific and actionable (e.g. "Tenant does not have the products feature entitlement (Features.ProductsManagement).", never a raw stack trace or generic 500).
Conclusion: no gaps requiring a code change were found. This pass is documentation + live verification only.
24.3 Package behavior matrix (live-confirmed, 2026-07-20)
| Package | Accounting chain (6 steps) | Product chain (3 steps) |
|---|---|---|
| Shumoul Starter | Blocked — no Features.AccountManagement | Planned/Succeeded — has Features.ProductsManagement |
| Shumoul POS | Blocked — no Features.AccountManagement | Planned/Succeeded — has Features.ProductsManagement |
| Shumoul Finance Starter | Planned/Succeeded — has Features.AccountManagement | Blocked — no Features.ProductsManagement |
| Shumoul Finance Advanced | Planned/Succeeded — has Features.AccountManagement | Blocked — no Features.ProductsManagement |
TenantOperationalDefaults is never gated by Features.AccountManagement alone (it resolves
Warehouse/CashAccount/SalesInvoiceTemplate via POS-or-accounting keys internally) — confirmed Planned for
all four packages live. CostCenters follows the onboarding needsCostCenters answer (or an explicit
override) independently of the rest of the accounting chain's entitlement gate, but still requires
Features.AccountManagement — so it is only ever reachable for the two Finance packages.
24.4 Live verification (this pass)
Dev host started clean, root-tenant admin login, real HTTP round trip through
api/Saas/TenantStarterKits/{Preview,Apply} (bridges enabled locally only, same loopback pattern as
Chapter 22 — never a production hostname).
Tenants: the four safe dev tenants from
Subscription Packages & Entitlements, Chapter 16
— 183581 (Starter), 510611 (POS), 171994 (Finance Starter), 793084 (Finance Advanced).
Preview — confirmed to write nothing (verified via before/after DB counts) and to match §24.3 exactly
for all four tenants in one pass, including the exact Blocked/Skipped/Planned status split per step.
Apply — idempotency proof, tenant 171994 (Finance Starter), CostCenters had never been applied to
this tenant before this pass:
| Step | 1st Apply | 2nd Apply (immediately after) |
|---|---|---|
| AccountingChartAccounts | Inserted=0, SkippedExisting=81 | Inserted=0, SkippedExisting=81 |
| AccountingChartMappings | Applied=0, Skipped=6, Unsupported=6 | identical |
| TenantOperationalDefaults | Created=0, SkippedExisting=2 | identical |
| CashBankAccountingLink | Applied=0, Skipped=2 | identical |
| InventoryAccountCategory | Applied=0, Skipped=13 | identical |
| CostCenters | Inserted=10, SkippedExisting=0 | Inserted=0, SkippedExisting=10 |
dbo.CostCenters for tenant 171994: 0 → 10 → 10 (confirmed via direct SQL — no duplication on the
second Apply). Two new Saas.TenantStarterKitApplyLogs rows were written (one per Apply call, per the
table's own "every commit call" design — never merged/deduplicated), both Status=Succeeded.
Apply — idempotency proof, tenant 183581 (Starter), product chain already applied in an earlier
phase:
| Step | 1st Apply (this pass) | 2nd Apply |
|---|---|---|
| TenantOperationalDefaults | Created=0, SkippedExisting=3, EntitlementSkipped=1 | identical |
| ProductCatalogPrerequisites | SkippedExisting=6, 0 created | identical |
| ProductCatalogCategories | Inserted=0, SkippedExisting=6 | identical |
| ProductCatalogItems | Items=0, SkippedExisting=2 | identical |
BusinessTypes/ProductDepartments/ProductCategories/Products/Warehouses/CashAccounts row counts
for tenant 183581: unchanged across both applies (1/1/6/2/1/1 before, during, and after).
Side effects: zero GeneralLedgerJournalEntries/PaymentVouchers/ReceiptVouchers/
StockItemTransactions/AccountOpeningBalances rows for either tenant across the entire pass — confirmed
via direct SQL, matching Operational Boundaries.
24.5 needsCostCenters link confirmed live
Tenant 171994 has no onboarding answer on file for cost centers. Requesting IncludeCostCenters: true
explicitly still applied the step, with an automatic warning surfaced in the response: "No onboarding
answer was found for cost centers for this tenant; applying anyway based on this explicit admin
request." — confirming an explicit request always wins over a missing/negative onboarding default, exactly
as designed.
24.6 Cross-reference: the shared-DB caching bug is confirmed fixed here too
Subscription Packages & Entitlements, Chapters 16–17
document a shared-DB cross-tenant AppSettings cache leak that first surfaced as a false
CashBankAccountingLink MissingDependency on this exact tenant pair. This pass's live Preview/Apply
calls against 171994/793084 (both on the same shared physical database) showed zero MissingDependency
false positives — CashBankAccountingLink correctly resolved SkippedAlreadySet for both tenants,
confirming the caching fix holds for Starter Kit's own call path too.
24.7 Test suites
No source changed in either repo, so the existing baselines apply unchanged — see Chapter 22 for the last full run. This pass's live verification is a supplement to, not a replacement for, that automated baseline.
24.8 Outcome
Tenant Starter Kit Preview/Apply is confirmed working correctly, idempotent, and entitlement-safe across
all four current subscription packages, with no code change required. Shumoul.Framework.MultiTenancy.Api
package version is unchanged.
