The isolation unit for Industry Extensions is an assembly, not a runtime module instance. Each project references only Domain, except Auction also references Finance. There is no Application/Contracts/Infrastructure split and no service-registration entry point.
1. Physical projects
| Project | Description claim | Actual content |
|---|---|---|
| Finance | Explicitly selected finance calculators | Five calculators, one bracket engine, one error catalog |
| HR | Explicitly selected HR calculators | LeaveCalculator, LeavePolicySchedule, error catalog |
| Auction | Explicitly selected auction calculator | AuctionCommissionCalculator and error catalog |
All target net10.0 with nullable and implicit usings. They have no PackageId, package version, SourceLink, or NuGet pack metadata. “Optional package” currently means selecting a ProjectReference, not consuming an independently versioned and signed commercial NuGet artifact.
2. Dependency direction
The old principle “general platform code never depends on an industry package” has an intentional exception: LegalCalculators directly references Finance. A genuinely neutral bracket primitive should move to a neutral calculator assembly, or the exception must be recorded.
3. Hosted governance marker
IndustryExtensionsModule lives in loaded BitzOrcas.Platform.Application, not in a calculator assembly. Its comment explicitly says the child assemblies are absent from the API composition graph and that the marker is hosted centrally so governance can discover it.
That creates a state in which the control plane lists IndustryExtensions but the data plane has no calculator assembly. A Registry, Operations view, or Profile catalog must not equate a contribution with runtime readiness.
4. No runtime enablement
There is no AddIndustryExtensions, IModuleServiceConfigurator, Feature key, Permission catalog, health check, or adapter-readiness probe. Once a consumer references an assembly, any code can call static methods directly.
Profile selection is therefore a build convention rather than an auditable runtime policy. Generated projects, license entitlement, features, composition evidence, and policy availability must form one readiness chain.
5. Consumption pattern
// The wrapper pins an approved policy version for this business use.var policy = await policyStore.GetPublishedAsync( jurisdiction, scenario: "finance.income-tax", occurredOn, cancellationToken);
// Static math handles numbers; the wrapper retains identity, tenant, and evidence.var raw = IncomeTaxCalculator.CalcIndividualIncomeTax(facts.TaxableIncome);if (raw.IsFailure) return raw.Error;
// A result ledger makes replay possible through input hash, policy, lines, and rounding.return await ledger.RecordAsync( facts.RedactedSnapshot, policy.Version, raw.Value, cancellationToken);The policy store and ledger are target integration components, not current library APIs. Keep tenant, database, and authentication concerns in an Application wrapper rather than polluting pure numeric functions.
6. Wrapper responsibilities
- authorize access to payroll, lending, contract, or auction facts;
- resolve Tenant, Office, Jurisdiction, and taxpayer/employee/contract category;
- select one published policy covering the occurrence date;
- validate units, currency, dates, and supporting evidence;
- call the pure engine and produce a breakdown;
- persist an immutable result ledger and audit trail;
- apply data minimization, encryption, retention, and erasure policy;
- localize stable Error codes through I18n;
- declare advisory versus formal use and show the correct disclaimer.
7. Stateless does not mean privacy-free
The libraries do not persist or log data, which is a useful boundary. Their inputs can still include income, deductions, loan principal, loss, maternity facts, and employment history. A wrapper can leak them through logs, trace tags, or exception messages.
Observe scenario, policy version, Error code, bounded numeric bucket, and latency—not raw salary, debt, or health-related values. Result ledgers need domain-specific retention rules.
8. Error boundary
Finance and HR catalogs span several first-segment prefixes; Auction fixes Auction. Descriptions are Chinese sentences, while the code is the stable contract.
var result = MortgageCalculator.CalcEqualInstallmentMonthly( principal, annualRate, years);
// Record only code and calculator, never the sensitive raw operands.if (result.IsFailure){ diagnostics.Count(result.Error.Code, calculator: "mortgage"); return Errors.Localize(result.Error.Code, languageContext.CurrentLanguage);}
// The owner decides whether and how the successful estimate is persisted.return result.Value;Some helpers return bare decimal or int and cannot express invalid input. Public wrappers must validate before calling them.
9. AOT claim
XML comments repeatedly claim zero reflection and Native AOT safety. The implementation does use basic numeric types, spans, lists, dates, and ValueObject without dynamic reflection. That is not evidence that a Native AOT host has published, trimmed, and executed these public APIs.
GA needs AOT publish, trim-warning, call-smoke, and public-API compatibility evidence, especially when policy deserialization is later added.
10. Versioning and distribution
A commercial optional package needs SemVer, compatibility policy, signing, SBOM, entitlement, SourceLink, symbols, scanning, and withdrawal policy. Rule version should remain distinct from engine binary version, so old policies can stay available for historical replay.
Replacing a static array in a DLL destroys historical reproducibility unless every previous binary remains deployable and identified on each result.
11. Runtime readiness probe
A target probe asks whether the assembly is loaded, wrapper is registered, license/feature allows use, a current published policy exists, the source is not expired, golden cases pass, and ledger/outbox is ready.
A governance contribution alone must not turn readiness green.
12. Test boundary
Pure libraries need unit, property, and golden tests. Wrappers separately need HTTP authorization, tenancy, policy resolution, audit, and idempotency tests. Distribution needs NuGet, AOT, template, and Profile tests. Do not add a database merely to call a pure formula.
The current test tree contains no references to any calculator method.
13. Inspection commands
# Project references and descriptions.rg -n "ProjectReference|Description" src/Platform/IndustryExtensions -g '*.csproj'rg -n "IndustryExtensions" src/Hosts -g '*.cs' -g '*.csproj'
# DI, permissions, features, endpoints, and tests should currently have no matches.rg -n "AddIndustryExtensions|PermissionCatalog|Feature|GenerateEndpoint|\[Fact\]" \ src/Platform/IndustryExtensions tests -g '*.cs'Industry Extensions overview · Finance calculators · Auction and GA