---
title: SAIF.Framework.Windows business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: <owner>
last_reviewed: 2026-09-15
library: SAIF.Framework.Windows
reference_type: package
---

# SAIF.Framework.Windows business rules

This page captures business rules enforced by this library, covering entry-point orchestration, argument resolution, command-line parsing, dependency registration, logging, and data-access composition.

## BR-WIN-001 Argument validation precedes application execution

| Field | Value |
| --- | --- |
| **Statement** | Argument validation runs before any application dependency registration or resolution. |
| **Trigger** | `ProgramRunnerBase.Run()` invoked. |
| **Enforcement** | `ProgramRunnerBase.Run()` |
| **Outcome** | `RegisterAppDependencies`/container build/`app.Run()` never execute if arguments are invalid. |
| **Source** | `Console\SAIF.Console\Console\ProgramRunnerBase.cs` |

---

## BR-WIN-002 Unconfigured argument properties must be convertible or explicitly configured

| Field | Value |
| --- | --- |
| **Statement** | Every settable property on an argument container not explicitly configured via `BuildArgumentResolvers` must be either auto-resolvable (`IConvertible`) or flagged as a validation error. |
| **Trigger** | `ArgumentContainerBase<T>.ResolveArguments()` called. |
| **Enforcement** | `ArgumentContainerBase<T>.HandleUnconfiguredArguments` |
| **Outcome** | Non-convertible unconfigured properties produce a `ValidationFailure`; convertible ones are silently auto-bound. |
| **Source** | `Console\SAIF.Console\Console\Arguments\ArgumentContainerBase.cs` |

---

## BR-WIN-003 Invalid arguments halt before application logic

| Field | Value |
| --- | --- |
| **Statement** | On invalid arguments, the process halts before running any application logic and requires a console keypress (`Console.ReadLine()`) before exiting. |
| **Trigger** | `Arguments.ResolveArguments().IsValid == false`. |
| **Enforcement** | `ProgramRunnerBase.Run()` |
| **Outcome** | `Environment.ExitCode = 1`; errors written to `Console.Error`; method returns without further processing. |
| **Source** | `Console\SAIF.Console\Console\ProgramRunnerBase.cs` |

---

## BR-WIN-004 SaifConsoleException supplies its own exit code

| Field | Value |
| --- | --- |
| **Statement** | A thrown `SaifConsoleException` (or subclass) determines the process exit code from the exception's own `ExitCode` property, not a fixed value. |
| **Trigger** | Exception of type `SaifConsoleException` escapes `app.Run()`/dependency resolution inside `ProgramRunnerBase.Run()`'s `try`. |
| **Enforcement** | `ProgramRunnerBase.Run()` catch clause |
| **Outcome** | `Environment.ExitCode = ex.ExitCode`; formatted exception text written to `Console.Error`. |
| **Source** | `Console\SAIF.Console\Console\ProgramRunnerBase.cs` |

---

## BR-WIN-005 Unhandled exceptions default to exit code 5

| Field | Value |
| --- | --- |
| **Statement** | Any unhandled, non-`SaifConsoleException` exception results in a fixed exit code of `5`. |
| **Trigger** | Any other `Exception` escapes the same `try`. |
| **Enforcement** | `ProgramRunnerBase.Run()` catch clause |
| **Outcome** | `Environment.ExitCode = 5`; formatted exception text written to `Console.Error`. |
| **Source** | `Console\SAIF.Console\Console\ProgramRunnerBase.cs` |

---

## BR-WIN-006 Successful Initialize resets exit code

| Field | Value |
| --- | --- |
| **Statement** | A successful `Initialize()` call always resets `Environment.ExitCode` to `0` before `Execute()` runs, regardless of any prior exit-code state. |
| **Trigger** | `ConsoleBase<TArgContainer>.Run()` invoked. |
| **Enforcement** | `ConsoleBase<TArgContainer>.Run()` |
| **Outcome** | `Environment.ExitCode = 0` set unconditionally between `Initialize()` and `Execute()`. |
| **Source** | `Console\SAIF.Console\Console\ConsoleBase.cs` |

---

## BR-WIN-007 Convertible properties auto-register

| Field | Value |
| --- | --- |
| **Statement** | Argument-container properties whose type implements `IConvertible` are auto-registered for parsing/validation even without an explicit `BuildArgumentResolvers` setup call. |
| **Trigger** | `HandleUnconfiguredArguments` runs during `ResolveArguments()`. |
| **Enforcement** | `ArgumentContainerBase<T>.HandleUnconfiguredArguments` |
| **Outcome** | A resolver is dynamically constructed via reflection (`BuilderSetupMethod.MakeGenericMethod`) and added to the resolver set. |
| **Source** | `Console\SAIF.Console\Console\Arguments\ArgumentContainerBase.cs` |

---

## BR-WIN-008 Non-convertible unconfigured properties fail validation

| Field | Value |
| --- | --- |
| **Statement** | Argument-container properties whose type does not implement `IConvertible` and are not explicitly configured are a validation failure, not a silent skip. |
| **Trigger** | Same as [BR-WIN-007](#br-win-007-convertible-properties-auto-register), `IConvertible` check fails. |
| **Enforcement** | `ArgumentContainerBase<T>.HandleUnconfiguredArguments` |
| **Outcome** | `ValidationFailure("{PropertyName} cannot be implicitly converted so it must be explicitly configured.")` added. |
| **Source** | `Console\SAIF.Console\Console\Arguments\ArgumentContainerBase.cs` |

---

## BR-WIN-009 Missing required arguments fail validation

| Field | Value |
| --- | --- |
| **Statement** | An argument configured as required (the `ArgumentResolver` default; not overridden via `NotRequired()`) that is absent from the parsed command line is a validation failure. |
| **Trigger** | `TryResolveArgument` invoked for a required, unresolved argument. |
| **Enforcement** | `ArgumentResolver<TArgumentContainer, TArgument>.TryResolveArgument` |
| **Outcome** | `ValidationFailure("{argumentName} is missing.")` added; property left unset. |
| **Source** | `Console\SAIF.Console\Console\Arguments\ArgumentResolver.cs` |

---

## BR-WIN-010 Argument value conversion failures fail validation

| Field | Value |
| --- | --- |
| **Statement** | An argument value present on the command line that fails its configured `propertyValueParser` conversion is a validation failure, not a thrown exception up the stack. |
| **Trigger** | `AssignArgumentValue` conversion throws. |
| **Enforcement** | `ArgumentResolver<TArgumentContainer, TArgument>.AssignArgumentValue` |
| **Outcome** | `ValidationFailure("{name} '{value}' is not a valid {type}.")` added; property left unset. |
| **Source** | `Console\SAIF.Console\Console\Arguments\ArgumentResolver.cs` |

---

## BR-WIN-011 Flag-only arguments default to true

| Field | Value |
| --- | --- |
| **Statement** | A two-token argument split (flag with no value, e.g. `-verbose`) is bound to the literal string `"true"`. |
| **Trigger** | `PatternCommandLineParser.Parse` splits a token into exactly 2 regex parts. |
| **Enforcement** | `PatternCommandLineParser.Parse` |
| **Outcome** | Dictionary entry `{standardizedKey: "true"}` added. |
| **Source** | `Console\SAIF.Console\Console\PatternCommandLineParser.cs` |

---

## BR-WIN-012 First occurrence of duplicate argument keys wins

| Field | Value |
| --- | --- |
| **Statement** | The first occurrence of a given standardized argument key wins; later duplicate keys in the same `args` array are ignored. |
| **Trigger** | `PatternCommandLineParser.Parse` encounters a key already present in the result dictionary. |
| **Enforcement** | `PatternCommandLineParser.Parse` |
| **Outcome** | No dictionary update occurs for the duplicate occurrence. |
| **Source** | `Console\SAIF.Console\Console\PatternCommandLineParser.cs` |

---

## BR-WIN-013 RegisterCommonDependencies requires non-null currentUserId

| Field | Value |
| --- | --- |
| **Statement** | `RegisterCommonDependencies` requires a non-null `currentUserId`. |
| **Trigger** | `AutofacExtensions.RegisterCommonDependencies` invoked. |
| **Enforcement** | `AutofacExtensions.RegisterCommonDependencies` |
| **Outcome** | `Enforce.ArgumentNotNull` throws if `currentUserId` is null. |
| **Source** | `Console\SAIF.Console\Console\AutofacExtensions.cs` |

---

## BR-WIN-014 Log file environment variables map to fixed log types

| Field | Value |
| --- | --- |
| **Statement** | Machine-level environment variable names are mapped to a fixed `LogType` set (`file0`–`file4`) by exact lower-cased name match; any non-matching name maps to `LogType.Custom`. |
| **Trigger** | `LogWriter` constructed by `ConsoleLogService`. |
| **Enforcement** | `LogWriter` constructor |
| **Outcome** | `LogType` assigned from the `knownLogTypes` lookup dictionary, defaulting to `Custom`. |
| **Source** | `Console\SAIF.Console\Console\LogWriter.cs` |

---

## BR-WIN-015 SQL Server has no stateless factory variant

| Field | Value |
| --- | --- |
| **Statement** | `SqlServerConfig`'s unit-of-work factory is always the single named `"sqlSever"` factory — SQL Server has no stateless-session variant in this framework (unlike Oracle). |
| **Trigger** | `SqlServerConfig.FactoryName` evaluated during `RegisterUnitOfWork`. |
| **Enforcement** | `SqlServerConfig.FactoryName` |
| **Outcome** | The `Stateless` flag inherited from `DatabaseConfig` has no effect on which factory a `SqlServerConfig` resolves. |
| **Source** | `Console\SAIF.Console\Console\DatabaseConfigs\SqlServerConfig.cs` |

---

## BR-WIN-016 Oracle factory selection driven by Stateless flag

| Field | Value |
| --- | --- |
| **Statement** | `OracleConfig`'s unit-of-work factory selection is driven by its own `Stateless` flag: `true` routes to `"oracleStateless"`, otherwise `"oracle"`. |
| **Trigger** | `OracleConfig.FactoryName` evaluated during `RegisterUnitOfWork`. |
| **Enforcement** | `OracleConfig.FactoryName` |
| **Outcome** | Container resolves the named `IUnitOfWorkFactory` matching the computed factory name. |
| **Source** | `Console\SAIF.Console\Console\DatabaseConfigs\OracleConfig.cs` |

---

## Notes
- No `ExitCode = 5` type is defined in this library; exit code `5` is reserved by [BR-WIN-005](#br-win-005-unhandled-exceptions-default-to-exit-code-5)'s generic unhandled-exception catch clause in `ProgramRunnerBase.Run()`.
- `ConsoleLogService`'s log-file discovery does not deduplicate machine environment variables pointing at the same file path; a commented-out duplicate-path check exists in source but is inactive and is not documented as active behavior.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [API reference](api/index.md)

## Sources
- `Console\SAIF.Console\Console\ProgramRunnerBase.cs`
- `Console\SAIF.Console\Console\ConsoleBase.cs`
- `Console\SAIF.Console\Console\Arguments\ArgumentContainerBase.cs`
- `Console\SAIF.Console\Console\Arguments\ArgumentResolver.cs`
- `Console\SAIF.Console\Console\PatternCommandLineParser.cs`
- `Console\SAIF.Console\Console\AutofacExtensions.cs`
- `Console\SAIF.Console\Console\LogWriter.cs`
- `Console\SAIF.Console\Console\DatabaseConfigs\SqlServerConfig.cs`
- `Console\SAIF.Console\Console\DatabaseConfigs\OracleConfig.cs`
