BitzOrcas.CodeGeneration.Cli (command name bitz-codegen) is the only tool carrying the “business-slice scaffolding” responsibility and one of just two PackAsTool tools in the repository. It has exactly one production entry point: --business-slice <schema.json>, which generates Contracts, Application, behavior tests, and trim-smoke skeletons from a strictly validated JSON slice definition.
The three historical free-form inputs (--inline, --module, --use-case) have not been deleted — they were demoted to audit entries: they generate zero C#, write a productionReady=false diagnostic manifest into .codegen-output/, and exit with code 2. If a command from an old tutorial yields no source files, that is not a bug; it is fail-closed doing its job.
Tool identity and installation
# Install as a dotnet tool from the local commercial feed (same channel as bitz-upgrade).dotnet new tool-manifestdotnet tool install BitzOrcas.CodeGeneration.Cli \ --version 1.0.0-alpha1 \ --add-source <commercial-feed>
# Or run from the repository without installing.dotnet run --project src/Tooling/BitzOrcas.CodeGeneration.Cli -- --helpHelp text is part of the template contract: verify-template.sh asserts that internal derived selectors never appear in help. Treat the help snapshot as authoritative during regressions.
Production entry: business-slice
One command, real effect (the slice is the repo fixture-shaped service-requests.v1.json; output captured from a real run):
The slice schema is fixed to an industry-neutral shape: one required-tenant unified aggregate plus a single Create command, with client-allocated Guid keys; domainEvents and integrationEvents arrays must be explicitly declared empty. Below is a LegalTech example isomorphic to the repository test fixture:
{ "schemaVersion": 1, "frameworkVersion": "1.0.0-alpha1", "module": { "name": "LegalCases", "baseNamespace": "BitzOrcas.Platform.LegalCases", "description": "Civil and commercial case module", "code": "legal-cases", "featureCode": "legal-cases.create" }, "aggregate": { "name": "MatterIntake", "pluralName": "MatterIntakes", "description": "Matter intake awaiting partner approval within a tenant", "tableName": "GeneratedMatterIntake", "idType": "guid", "idAllocation": "client", "tenancy": "required", "softDelete": true, "concurrency": true, "fields": [ { "name": "CaseNumber", "description": "Court or arbitration case number", "type": "string", "required": true, "maxLength": 64, "normalize": "trim", "errorMember": "MatterIntakeInvalidCaseNumber", "errorCode": "LegalCases.MatterIntake.InvalidCaseNumber" }, { "name": "DisputeAmount", "description": "Amount in dispute", "type": "decimal", "required": false, "minimum": 0, "maximum": 100000000000, "normalize": "none", "errorMember": "MatterIntakeInvalidDisputeAmount", "errorCode": "LegalCases.MatterIntake.InvalidDisputeAmount" } ] }, "create": { "commandName": "SubmitMatterIntakeCommand", "description": "Submit a matter intake within the current tenant", "route": "/api/matter-intakes", "resourceType": "matter-intake", "permissionMember": "SubmitMatterIntake", "permissionCode": "legal-cases.matter-intake.create", "tenantErrorMember": "TenantRequired", "tenantErrorCode": "LegalCases.Tenant.Required" }, "domainEvents": [], "integrationEvents": []}Every field pairs a mandatory errorMember/errorCode — generated validation rules carry strongly-typed error codes so bare-string failures cannot return. Invocation:
dotnet bitz-codegen --business-slice ./design/matter-intakes.v1.json \ --output .codegen-outputAn existing target directory refuses to be overwritten; there is no OverwriteStrategy knob. To regenerate, clear the previous output and review the diff first.
Manifest evidence fields
A successful run records more than a file list:
| Field | Meaning |
|---|---|
Status | ready or blocked; blocked means a reviewable diagnostic result, not a crash |
ProductionReady | production marker; always false for audit entries |
SchemaSha256 | hash of the input slice — pins which definition produced this output |
SourceGeneratorOwned | wiring owned by source generators: dependency-injection, endpoint, orm-fluent-configuration, module-catalog |
ExplicitlyUnsupported | shapes v1 deliberately does not generate: update-command, query, domain-event, integration-event |
SourceGeneratorOwned explains why you will not find DI registrations or endpoint files among outputs — Roslyn generators own those at compile time, and hand-writing them creates a second source of truth. Writing is two-phase: a temp directory is completed and then placed atomically via Directory.Move; on IO failure the temp directory is removed and BOCG310 is reported while the target stays byte-identical.
Audit entries: the old shapes, fail-closed
Three historical inputs remain callable, but their role changed:
# Is this a valid command? Not a production path: it writes an audit# manifest and exits 2.dotnet bitz-codegen --inline \ --module-name Tracker \ --aggregate MatterIntake \ --property 'CaseNumber:string:64' \ --property 'DisputeAmount:decimal'echo $? # 2; .codegen-output contains only a manifest, zero C#| Legacy entry | Current behavior |
|---|---|
--module <json> (aggregate mode) | writes a productionReady=false diagnostic manifest, exits 2 |
--inline --module-name … --aggregate … | same |
--module … --use-case … (use-case mode) | same |
--with-endpoint | explicitly rejected; handwritten endpoints are gone |
--dry-run / --no-staging | rejected at every entry; the tool never writes into the source tree |
These entries exist so legacy scripts, courseware, and old READMEs get a deterministic “you are outdated” answer instead of silently producing half-products that violate current architecture.
Error-code families
| Family | Meaning | In common |
|---|---|---|
BOCG1xx / BOCG2xx | legacy-input audit (the three entries above) | zero C# output |
BOCG3xx | production schema validation failed | zero writes |
Automation should key on exactly two signals: the process exit code and the manifest’s ProductionReady field — never human-readable text.
Human landing process
- Settle business keys, permission codes (
{module}.{resource}.{action}), and error-code ownership in design review before writing the schema — the schema is the contract and enters theSchemaSha256evidence chain. - Run generation; check the target directory and confirm
ready/ProductionReady=truein the manifest. - Wire generated projects into the Consumer Solution: add to
.slnx; governance joins via the owner directory’s[AppModule]marker and itsDependsOnedges. - Fill behavior tests with real fixtures and assertions; the slice ships a trim smoke — make sure it runs inside your CI trim matrix.
- Complete XML doc comments and data-scope rules, then run Application, Architecture, and Integration gates.
- Remove the
.codegen-outputstaging copy; the generated directory is the single source of truth.
Verification commands
# Generator templates, atomic placement, and manifest contracts.dotnet test tests/BitzOrcas.CodeGeneration.Tests --configuration Release
# Generator-as-package contract consumed by an isolated project.dotnet test tests/BitzOrcas.Generator.Package.Tests --configuration Releasetests/BitzOrcas.CodeGeneration.Tests/Fixtures/service-requests.v1.json is the official fixture; every field in this page’s example maps one-to-one onto it. When schema behavior is unclear, trust the fixture and its assertions — do not guess.
Common mistakes
- Treating the legacy
--inlinewalkthrough as the production path and discovering exit code 2; - leaving last-run artifacts in the target directory and getting a wholesale refusal;
- expecting update/query or domain-event output —
ExplicitlyUnsupportedsays otherwise on paper; - omitting the event arrays instead of declaring them empty;
- treating a
blockedmanifest as a failure to retry blindly instead of reading its diagnostics.