A caller learning BitzOrcas’s generic interfaces should not first have to understand a vertical industry’s objects, regulations, roles, or historical database column names. Legal, Finance, HR, and Auction capabilities live in explicit opt-in modules or commercial packages; the Framework shared kernel, generic-Profile platform modules, and Host registration stay industry-neutral.
What must stay vertical-free
src/Framework/**- generic-Profile-default platform modules
src/Hosts/**registration/config/display text for generic composition- the Consumer Solution and
src/Modules/Sandbox/**example code
Ownership rules
- Vertical capabilities (Legal/Finance/HR/Auction) go into an explicit opt-in module or commercial package — never the Framework shared kernel.
- A vertical module’s types, error codes, and enums are published by the owner package; the default Host does not register runtime enums/adapters for unselected packages.
- Generic module CLR names, persistence columns, config keys, error text, notification templates, and ops diagnostics use industry-neutral terms.
- Examples show only generic business actions plus direct dependencies; vertical examples live in the opt-in module’s own tests and docs.
- A historical physical column with industry semantics is migrated to a neutral column name with an explicit, idempotent, stop-on-conflict data migration — never relying on ORM auto-column-creation to force manual data movement.
- Rule dates, holidays, and dynamic standards belong to the calling interface; a pure-calc module must not implicitly read process current time or fake dynamic regulations as fixed defaults.
Current ownership
| Capability | Owner | Default surface |
|---|---|---|
| General + loan interest calc | IndustryExtensions.Finance | not in Framework or generic Profile |
| Legal deadline + court case number | LegalCalculators | not in Framework or default Host enum catalog |
| Legal fees, statute of limitations, damages | LegalCalculators | explicit opt-in only |
| HR leave policy + date rules | IndustryExtensions.Hr | pass effectiveFrom explicitly; does not read process date |
| Website contact lead owner | Website.AssignedOwnerId | CLR + physical column both neutral names |
The AssignedLawyerId → AssignedOwnerId migration
WebsiteLead.AssignedLawyerId exists only as a legacy DB upgrade input. The migration script 20260716-website-owner-industry-neutral.sql refuses to continue if both old and new columns exist simultaneously, avoiding silently overwriting owner data.
Template adoption contract
The template exposes a single coupled preset parameter, ProfileChoice. The legal space is mini-api-single, default-business-{single|multi}-{none|finance|hr|auction|legal}, with a default of default-business-single-none. A mini+api+industry combination cannot be expressed by the public parameter; the old --IndustryExtension flag is unknown and fails before any disk write. Each industry preset adds exactly one root PackageReference to the Business Starter Application and generates an IndustryExtensionAdoptionTests.cs that calls the selected package’s public API directly. The four industry packages never enter the Profile.Mini.Api or Profile.Default.Business default closure.
Adopting an industry preset takes one command; invalid combinations are rejected before anything touches disk:
# Generate a multi-tenant business Host with the legal preset; the template validates# ProfileChoice before writing any file.dotnet new bitzorcas-host --ProfileChoice default-business-multi-legal --output LegalHostThe generated solution carries IndustryExtensionAdoptionTests.cs, which calls the legal package’s public API directly — turning “the vertical package is really consumed” into a testable fact instead of a review promise. Full-chain verification of template output is handled by the framework repository’s scripts/build/verify-template.sh (install template → generate per ProfileChoice combination → cold-cache restore/build/test).
The three unselected industry packages never appear in generated project references — neutrality is proven by an empty closure, not asserted by comments.
Source evidence boundary
| Fact to prove | Owner evidence to inspect |
|---|---|
| Framework has no vertical types | namespaces, public API, and dependency graph under src/Framework |
| Default Hosts do not adopt a vertical implicitly | Host references, composition, and generated Profile closure |
| Vertical capability ships from its owner | IndustryExtensions, LegalCalculators, and connector contracts |
| Website ownership is neutral | AssignedOwnerId aggregate, metadata, and both ORM migrations |
| Legacy-column upgrade preserves data | stop-on-conflict, copy, rerun, and migration-catalog tests |
| Template cannot express an invalid Profile | template parameter schema, clean-cache creation, and AdoptionTests |
Keyword scans alone cannot prove neutrality. A generically named interface that returns a legal-industry DTO, or a default Profile that receives a vertical through a transitive package, still violates the boundary. Review public API, package closure, generated templates, and runtime registration together.
Change-review questions
- Is the new type a generic platform fact, or meaningful only inside one industry?
- Do its name, errors, configuration, columns, and diagnostics use the same neutral vocabulary?
- Are time, jurisdiction rules, and policy versions explicit inputs?
- Can the default Host, Mini Profile, and Consumer Solution still build without a vertical package?
- Does the optional package integrate only through contracts, options, policies, store ports, or events?
- Is migration behavior deterministic when both columns exist, the old column is absent, or the script reruns?
- Does an industry preset add one root package and call its public API from an isolated consumer test?
- After removing the package, are there no residual registrations, enums, or required settings?
Review commands
# Neutral migration, template preset, and explicit-adoption evidence.rg -n "AssignedLawyerId|AssignedOwnerId|ProfileChoice|IndustryExtensionAdoptionTests" \ src scripts tests -g '*.cs' -g '*.json' -g '*.sql'
# Default project closures must not acquire the four vertical roots accidentally.rg -n "IndustryExtensions|LegalCalculators" \ src/Tooling/BitzOrcas.Modern.Templates -g '*.csproj' -g '*.json'