Run lifecycle¶
Main constructs Program and calls Run(). Because ArgumentContainer declares no properties, argument resolution always succeeds and the application always reaches Execute(), which performs the entire keyword-sync operation before Finish() runs as a no-op.
Execution flow¶
flowchart TD
MAIN[Main] --> RUN["Program.Run()"]
RUN --> ARGS["CreateArgumentContainer(args)<br>new ArgumentContainer(args)"]
ARGS --> RESOLVE["Arguments.ResolveArguments()<br>always succeeds - no arguments declared"]
RESOLVE -->|Invalid - unreachable here| FAIL["Write failures to Console.Error<br>ExitCode = 1<br>application never runs"]
RESOLVE -->|Valid| REG["RegisterAppDependencies(builder)"]
REG --> BUILD["Build container + CreateApplication(context)<br>new OnBaseKeywordSyncRunner(...)"]
BUILD --> INIT["Initialize()<br>no-op"]
INIT --> ZERO["ExitCode = 0"]
ZERO --> EXEC["Execute()<br>keyword sync operation"]
EXEC --> FIN["Finish()<br>no-op"]
FIN --> DISPOSE["Dispose() + WriteFooter()"]
DISPOSE --> ECHO["Print 'Exit Code: n'"]
EXEC -.->|Unhandled Exception| ERR5["ExitCode = 5"]
Argument resolution¶
ArgumentContainer declares no properties, so BuildArgumentResolvers adds nothing beyond the inherited base call. ResolveArguments() therefore always succeeds, and the exit-code-1 argument-validation path is unreachable for this application*. See Arguments for the complete (empty) contract.
Dependency registration¶
RegisterAppDependencies calls builder.RegisterCommonDependencies(Environment.UserName, new[] { typeof(Program).Assembly }) with no DatabaseConfig arguments, so no unit-of-work or database connection is ever registered. It also registers SAIF.Services.LogService as ILogService (never resolved) and calls builder.ConfigureOpenTelemetry(options => { options.Owner = "Documents"; }). See Architecture and Dependencies for the full registration table.
Initialize¶
OnBaseKeywordSyncRunner.Initialize() has an empty body — no resource acquisition or preflight checks. The Imaging API client is instead constructed inside Execute().
| Step | Action | On failure |
|---|---|---|
| 1 | No-op | — |
Execute¶
The entire business operation runs in Execute(). A one-paragraph summary follows; the step-by-step logic is documented per operation under Operations and processes.
Processing pattern¶
| Step | Action | On success | On failure |
|---|---|---|---|
| 1 | Log start, read OnBaseKeywordSync.onbaseasync, construct ImagingApiClient |
Continue to clearing invalid person syncs | Exception → LogError/LogException → rethrow → exit 5 |
| 2 | Clear invalid person keyword syncs, then retrieve and order unprocessed keyword syncs | Continue to sync | Exception → rethrow → exit 5 |
| 3 | Sync the batch with OnBase (per-record decision, send, mark processed) | Log per-record output, batch completes | Any record's failure aborts the remaining batch — no per-record isolation* |
Finish¶
OnBaseKeywordSyncRunner.Finish() has an empty body — no explicit teardown, totals, or notification is emitted. This is a finding, not an omission.
Exit codes¶
Exit codes are inherited from the SaifConsoleException hierarchy. The table below records which conditions in this application produce each code.
| Exit code | Exception | Triggered by |
|---|---|---|
| 0 | — | Execute() and Finish() complete without throwing |
| 1 | Argument validation / BadParameterException |
Not used — no arguments are declared and no BadParameterException is thrown* |
| 2 | DatabaseErrorException |
Not used — no database access exists |
| 3 | DataErrorException |
Not used |
| 4 | FileInputOutputException |
Not used |
| 5 | Unhandled exception | Any exception rethrown from Execute() — Imaging API HTTP failure, OnBase WCF fault/timeout, missing customquerymappings entry, MarkProcessed failure, or a configuration key that fails to convert |
| 6 | NoDataFoundException |
Not used — an empty unprocessed set simply logs a zero-record summary and exits 0 |
| n/a | CustomErrorException |
Not used — no CustomErrorException is constructed anywhere |
In practice the observable contract is binary: 0 on success, 5 on any failure.
Error handling¶
Recoverable errors¶
None. There is no per-record error isolation: one failed record aborts the remaining batch, and already-processed records are not rolled back*.
Fatal errors¶
OnBaseKeywordSyncRunner.Execute wraps the whole operation in a try/catch (Exception ex) that logs the message, calls LogService.LogException(ex), then rethrows. ProgramRunnerBase.Run catches the rethrown exception; since the application never throws a SaifConsoleException subclass, every failure maps to exit code 5†.
Related documentation¶
- Application index
- Overview
- Architecture
- Arguments
- Dependencies
- Configuration
- Operations and processes
- Business rules
- Operational runbook
- Tech debt
Note: Statements marked with
†are derived from dependency library documentation via the Legends CLI. Statements marked with*were inferred from code structure or contextual evidence.
Sources¶
OnBaseKeywordSync/Program.csOnBaseKeywordSync/OnBaseKeywordSyncRunner.csOnBaseKeywordSync/Services/ImagingApiClient.csOnBaseKeywordSync/Services/KeywordUpdater.cs