The traditional way to add a capability to a platform: create a project, write some code, sprinkle registrations into some god-sized startup class, and pray nobody deletes them. Six months later nobody can say what execution surfaces exist or which default implementation silently swallows production traffic. BitzOrcas intake goes the other way — every addition passes through one repeatable, fail-closed, observable, testable admission contract, guarded by templates, source generators, architecture rules, test gates, and docs, so the same steps produce the same result for every contributor.
Capabilities
| Capability | Scope | Gate |
|---|---|---|
| EXT-1 | Intake contract, fixed verification, review checklist | ExtensionIntakeTests |
| EXT-2 | Incomplete module/aggregate input → zero C#, diagnostic list, no template backflow | ModuleGenerationFailClosedTests |
| EXT-3 | New modules register governance via compile-time attributes, fail-closed | ModuleGovernanceRegistrationTests |
| EXT-4 | Adapter matrix / default port ledger / operations probe consistency | AdapterMatrixConsistencyTests |
| EXT-5 | Thin endpoint protocol adapters; ProblemDetails/correlation/audit through the unified pipeline | Endpoint Source Generator + API smoke |
| EXT-6 | Background job intake, JobHost envelope, operations job smoke | BackgroundJobIntakeTests + JobHost unit tests |
| EXT-7 | dotnet new bitzorcas-host template + full verify-template.sh chain | DotnetNewTemplateFlowTests |
| EXT-8 | Docs/architecture rules/test gates/final closure of standard intake | This contract + fixed verification |
EXT-2 deserves its own sentence: feed the generator an incomplete module definition and it produces not “half-broken code, fingers crossed” but zero C# output plus a diagnostic list precise about each missing item, with no template backflow files — bad input is rejected at the source.
Global rules
- Application does not depend on
HttpContext, ORM, CAP, Redis, RabbitMQ, or any concrete infrastructure type. - The Host is only the composition root and runtime wiring — no business rules.
- New extensions fail closed by default: without production configuration, the API Shell/JobHost starts with stable off-semantics or gives a fail-fast diagnosis.
- Every new extension is visible in at least one of: Operations, health/config diagnostics, trace/log/audit.
- A new extension updates implementation + architecture tests + smoke/integration tests + documentation together. The CLI generates no extension code.
- Module/aggregate and use-case CLIs write diagnostic lists to staging only; writing directly into the source tree fails closed.
- Doc/code conflict means fix the gate first, then fix implementation/docs — never just the words.
Module intake
The simple golden path starts from Contracts (or a standalone Domain owner) + Application. Do not pre-build empty Infrastructure/Endpoints. Request/Handler/short Rules are local; the Handler orchestrates caller/tenant, aggregate behavior, event registration, persistence, and Result.
Module identity is declared through compile-time markers — one XxxModule.cs marker type in the owner project plus owner-local catalogs:
namespace BitzOrcas.Platform.Tracker.Contracts;
using BitzOrcas.Modularity.Governance;
// Identity trio: display name, stable Code (permission prefix {code}.{resource}.{action}), base namespace.[AppModule("Tracker", Code = "tracker", BaseNamespace = "BitzOrcas.Platform.Tracker")][DependsOn("Authorization")] // Every dependency edge is explicit; implicit transitive coupling is banned[DependsOn("Files")]internal sealed class TrackerModule;
// Permission ownership registers in the owner-local catalog; the Governance Generator reads both// at compile time and emits GeneratedModuleContribution_Tracker implementing IModuleContributionProvider,// with collection fields sorted by Ordinal — catalog output stays byte-deterministic, diffable, auditable.Nothing scans assemblies at runtime: all governance facts come from generator projection. Full marker semantics, the current 36-marker inventory, and the legacy-ledger ratchet live in Module governance.
Endpoint/DI/ORM Fluent Config are owned by generators; simple aggregates use generated repositories directly.
The deep-module growth path extends physical surfaces only when real complexity appears: an independent public ReadModelStore for list/filter/pagination/projection; a dedicated QueryStore + adapter for joins/group-by/reports/external queries; Infrastructure for external protocols/resource lifecycles/provider asymmetry; thin Endpoint/API adapters only when the transport source generator cannot express the need. Splitting physical surfaces prematurely is over-engineering by another name — let the slice run first, then let structure grow.
Adapter intake
- Define ports in Application or Contracts; ports must not expose
IQueryable/DbContext/ISqlSugarClient/CAP/Redis/RabbitMQ. - Declare default semantics explicitly:
InMemory*— API Shell/local development/tests only, never production.Null*— intentional silent no-op (e.g. a disabled event publisher).Unavailable*— loud failure/fail-closed; callers get a stable error orResult.Failure.
- Default/Unavailable adapters register through
TryAdd*; production/optional adapters register explicitly and override defaults. So an unconfigured production environment gets deterministic no-op or loud failure, not an unexpected implementation winning the race. - ORM adapters use
[RegisterOrmAdapter<TPort>]; the generator emits standard fail-closed default ports verified by provider contract/consistency tests. - Runtime visibility flows into operations adapter probes, config diagnostics, and health readiness — an adapter’s presence ultimately shows up in the
/api/operationsprobe matrix. - Keep the platform adapter matrix and ORM capability matrix in sync; drift in each is guarded respectively by
AdapterMatrixConsistencyTestsandOrmAdapterParityTests.
Background job intake
Jobs live in src/Hosts/BitzOrcas.JobHost; the API Host carries no scheduled work. A job implements IJob (or equivalent), routing its Execute through QuartzJobExecutionAuditor to emit Activity/CorrelationId/BackgroundJob audit trail plus structured logs, with JobHost composition registering JobKey, trigger, schedule, and options explicitly. A quasi-production environment lacking persistent storage/Redis/OTLP fails fast or closes under config diagnostics. /api/operations/jobs describes job names, sources, enabled state, schedules, and runtime visibility. Jobs must not capture scoped dependencies directly — they create scopes or take safely-lifecycled constructor injection.
Endpoint intake
Endpoints use [GenerateEndpoint] or IEndpoint; route handlers are thin protocol adapters. Authorization defaults on; anonymity requires justification and tests. Business authorization lives in Application’s IAuthorizedRequest/rules, never in endpoints. All failures go through ResultExtensions.ToHttp or the unified exception handler into ProblemDetails, with CorrelationId middleware applied automatically. Required tests cover 401, 403, success/fail-closed, ProblemDetails shape, and API doc smoke.
Fixed verification block
# Run the build, docs, architecture, application, and API shell gates used for intake, in order.dotnet build BitzOrcas.Modern.slnx --no-restorescripts/build/check-xml-files.sh --no-buildscripts/build/check-xml-comments.sh --no-build# Architecture regression and behavior regression leave evidence separately, easing failure triage.dotnet test BitzOrcas.Architecture.Tests --no-builddotnet test BitzOrcas.Application.Tests --no-builddotnet test BitzOrcas.Integration.Tests --no-build --filter ApiShellgit diff --checkSlices touching code generators, JobHost, workflow jobs, templates, or provider adapters must run their additional tests.