Software Architecture
Purpose
The wiki describes the platform three ways, and each has its page: what the software is (this page — applications, libraries, plug-ins, schema, UI, infrastructure, one by one), what the agent framework provides to anyone who writes an agent (Agent Framework), and which agents exist and how they talk (Agents). This page is the first: every deployable and every library, its responsibility, its interfaces, its dependencies, and where its source comes from (§ 9). Nothing here is built yet; every row is P unless marked.
0. Solution placement [P, layout mirrors the proven AI module — F]
The platform is a module in serdica-backend, laid out exactly like Ablera.Serdica.AI on the codex/health-ai-csharp-architecture branch (own solution, App host, libraries, customer plugins, tests, per-module AGENTS.md):
src/Serdica/Ablera.Serdica.AI.Support/
AGENTS.md ← module guidance (estate convention: root AGENTS.md on master; 40 AGENTS.md on the AI branch)
Ablera.Serdica.AI.Support.sln
App/Ablera.Serdica.AI.Support.Webservice/ ← the host: Program, Endpoints, Runtime, Governance
__Libraries/
Ablera.Serdica.AI.Support.Persistence/ ← SRD_SUPPORT schema: versioned DbScripts, stores, retention
Ablera.Serdica.AI.Support.Sessions/ ← case/session/task state machines
Ablera.Serdica.AI.Support.Connectors.*/ ← one library per external system (tools)
__Plugins/ ← customer-specific packs (Bulstrad skills, prompt catalogs)
test/
1. The component map
flowchart TB UI["Operator and client UI"]:::owner API["Webservice API
HTTP and SignalR"]:::work RT["Durable runtime
scheduler and workers"]:::work MD["Model adapter
IInferenceClient"]:::work MEM["Profiles, skills
and owned knowledge"]:::learn G["Authority and audit"]:::decision EX["Deterministic executor
connector adapters"]:::work DB["Platform records
SRD_SUPPORT and papers"]:::store Q["Inbox and outbox
Rabbit delivery"]:::store T["Customer systems
and external owners"]:::store P["Customer plug-in
scope and domain bindings"]:::store UI --> API API --> RT P -.->|"bind at startup"| RT RT -->|"one model turn"| MD MEM -->|"pinned versions"| RT RT -->|"commit state"| DB RT -->|"durable messages"| Q RT -->|"proposed effects"| G G -->|"exact permit"| EX EX -->|"apply and verify"| T classDef work fill:#eef4ff,stroke:#6889ba,color:#17365b,stroke-width:1.3px classDef decision fill:#fff4df,stroke:#b78c36,color:#65470d,stroke-width:1.5px classDef proof fill:#e9f5ef,stroke:#689b81,color:#224e39,stroke-width:1.3px classDef learn fill:#f1edf9,stroke:#9580b9,color:#534172,stroke-width:1.3px classDef store fill:#f5f7fa,stroke:#98a6b7,color:#34445a,stroke-width:1.2px classDef owner fill:#24486b,stroke:#24486b,color:#ffffff,stroke-width:1.4px click UI href "ui.html" "The existing two surfaces use HTTP and committed realtime projections." click API href "contracts/openapi.yaml.html" "One API validates identity, commands and concurrency." click RT href "50-agents/agent-runtime.html" "One deployable image, with API and worker roles." click MD href "50-agents/model-execution.html" "An LLM proposes typed actions; the owned adapter is replaceable." click MEM href "50-agents/agent-framework.html" "Versioned authoring, memory and evaluation." click G href "gating.html" "The auditor reviews; gates and grants control target effects." click EX href "contracts/Connectors.Abstractions.cs.html" "Only registered, granted operations reach target systems." click DB href "data-model.html" "Durable tasks, call journal, decisions, evidence and immutable request bytes." click Q href "50-agents/agent-runtime.html" "Deduplicate messages and recover without replaying successful effects." click T href "trust-and-data.html" "Target estate is separate from platform persistence; owner work returns evidence." click P href "whitelabel-catalogue.html" "Channels, target capabilities, product vocabulary and customer policy."
Runtime responsibilities, not a project dependency graph. The library table below owns code dependencies and reuse choices. Target databases are deliberately separate from platform state.
2. Applications
| Application | Responsibility | Interfaces in | Interfaces out | Source |
|---|---|---|---|---|
Ablera.Serdica.AI.Support.Webservice (src/Serdica/Ablera.Serdica.AI.Support/App/…) |
the single deployable: hosts the runtime, the governance API, the gate/grant service, the SignalR hub, the Rabbit lanes; runs worker replicas of the same image with AcceptCases=false on the API replica |
HTTP (Authority JWT) for the UI; SignalR (case-keyed ticket); Rabbit lanes ai_support.request, ai_support.cancel; monitors (Jira JQL sweep, mailbox scan, HDesk queue) on a schedule |
SRD_SUPPORT (runtime principal); connectors to every external system; provider endpoint; fluentd stdout; OpenTelemetry |
new; host plumbing linked from the estate; Program.cs shape from McpProxy and the health Manager (SERDICA_ env prefix, Serilog, health checks, EndpointsRegistration) |
serdica-ui ai-support section |
the operator workspaces and the client app (UI) | Authority login; the webservice HTTP + SignalR | — | new Angular remote in the QA UI build |
| Developer instance | the same webservice run on an Ablera machine over VPN against QA (Software Architecture § 10.6) | as above | as above | same binary, qa-connected configuration |
P There is no separate worker application: a worker is the same image with a different role flag, so one Dockerfile, one CI job, one compose unit with two service entries.
3. Libraries — ours
P Ownership exposed by the case journeys: AI.Support.Sessions evaluates the execution-obligation join and scoped external responses through ports in AI.Support.Contracts; Papers supplies immutable revisions, Connectors supplies effect evidence and Memory supplies versioned applicability/use records. Runtime schedules external waits, restoration and post-result tasks through the same durable machinery. This adds no family-specific service or scheduler. UI projections read case papers and events; an LLM verdict cannot directly set resolved.
P Schema scope aliases. Names such as oracle-iprod, oracle-sys and oracle-integr in stage inventories denote scoped Oracle operations/credentials, not a requirement to build three additional driver libraries. The connector registry resolves them to the appropriate Oracle handler and explicit schema/environment scope. An unregistered alias is refused, never reflected into a class name.
| Library | Responsibility | Public surface | Depends on | Notes |
|---|---|---|---|---|
AI.Support.Sessions |
the case / task / turn model and their state machines; parking, resumption, control transfer; impact keys and coordination | ICaseService, ITaskService, TurnCheckpoint, CaseStateMachine, TaskStateMachine (pure, from the copied Workflow.StateMachines shape) |
Persistence, Ledger | Agent Runtime; the graph is TASKS rows (Agent Runtime § 1); case/session states are Agent Runtime § 11 |
AI.Support.Ledger |
append-only audit and usage records with prev_hash / record_hash (canonicalisation from AI.Contracts), periodic seal, correction records, projections (evidence graph, metrics) |
ILedger.Append(event), ILedgerQuery, ISealer, IMetricsProjector |
Persistence, AI.Contracts | Architecture § 3.2; DB triggers enforce append-only |
AI.Support.Papers |
the token-gated file service for working papers: plan, contract artefact, write log, build state, MISSING, report; revisions; per-case listing; every access audited | IPaperStore.Read/Write/List(caseToken), PaperToken issuer |
Ledger, Persistence (index), host volume | Agent Runtime § 11.2; deliberately not FileServer V2 |
AI.Support.Memory |
the memory tree: AGENTS.md per node, articles with states proposed / active / retired, provenance, version pinning, the experience sub-index, retrieval (grep index first; vector optional) |
IMemoryTree.ReadNode, IMemoryTree.Search, IMemoryWriter.Propose/Activate/Retire, IExperienceIndex |
Persistence (metadata), a git-backed or volume store for article bodies, Ledger | Agents Memory, Agents Memory § 7.4b |
AI.Support.Connectors.Abstractions |
the connector contract: operation catalogue with capability descriptions, trust-labelled result envelopes, environment scoping, dry-run, effect class, idempotency key | IConnector, ConnectorOperation, ToolResultEnvelope, EnvironmentScope |
AI.Contracts | Failure and Recovery § 4, Agent Runtime § 2 |
AI.Support.Connectors.OracleIpal · .OracleInsis |
select / write shape A (collapsed) / write shape B (leased) / PL/SQL snapshot + signature / PL/SQL apply with precondition; session budget per environment; statement logging, never rows | operations as data | Connectors.Abstractions, ODP.NET | Failure and Recovery § 1, Stage Planning § 3b |
AI.Support.Connectors.AbacusGateway |
getRates (read, computes nothing), template resolution check | HttpClient | Stage Abacus | |
AI.Support.Connectors.Jira · .Mail · .HDesk |
intake normalisation to the case shape; fetch; gated comment / transition / send; handle substitution for personal data at the boundary; the channel mechanics (ADF, provenance stamps, OTRS article types) | Atlassian REST, Microsoft Graph, OTRS | seeds: the v1 C# CLIs aisa-jira, aisa-mail, aisa-hdesk (Baseline § 2) |
|
AI.Support.Connectors.Ssh |
host only (exec_readonly on an allow-list; exec_privileged as a gated write; no sudo on the platform key; probe). There is no tunnel connector (D132, 04.09.2026): every target is reached directly from the QA host, and reach is data — CONNECTOR_SCOPES rows per environment × system with route.kind ∈ {direct, unreachable} (the probe's result); a target the probe cannot reach parks the case on a capability gap and becomes a specific network request to the estate, never a hop through another host (D46, D128). Vault SSH certificates with a per-session TTL |
operations as data | Connectors.Abstractions, SSH.NET or the OpenSSH client | seed: the v1 ssh-mcp servers; Software Architecture § 10.3 |
AI.Support.Connectors.GitLab · .Kibana · .RabbitMq · .Browser · .BiPublisher · .Camunda |
worktrees, MR/commit; log search by request id; broker inspection; UI driving for verification; runReport; process definitions/instances (via Workflow.Tasks) |
respective APIs | Agents Memory and Skills § 10 | |
AI.Support.Persistence |
SRD_SUPPORT DbScripts (001…, install-clean, upgrade, grant-runtime, verify-*), EF model, stores for every table of Architecture § 3.1, retention evaluator (D109) |
SupportDbContext, I*Store |
EF Core 9, Oracle EF provider | pattern copied from AI.Persistence, schema is ours (Software Architecture § 9.2) |
AI.Support.Providers |
governed route selection with the module data-boundary predicate; adapter registry; usage metering into the ledger | IRouteSelector, IInferenceClient (the inspected copied seam; Model Execution § 2) |
AI.Abstractions, AzureOpenAI adapter — both copied and owned (D39) | Agents § 0.3; Agent Runtime § 9.3 |
AI.Support.Runtime |
the turn loop, context assembly, compaction, permission pipeline, tool middleware, spawn/messages, checkpoints, budgets | ITurnEngine, IPermissionPipeline, IToolPipeline, ISpawner, IMessageBus (in-process + Rabbit) |
Sessions, Ledger, Papers, Memory, Connectors.Abstractions, Providers | Agent Runtime |
AI.Support.Governance |
profiles, prompts, skills, eval sets: draft → test → publish → retire; ownership; publisher role; eval runner; replay tests | IProfileCatalogue, IPromptGovernance, ISkillCatalogue, IEvalRunner |
Contracts (IEvaluationExecutor port) | Agent Framework; lifecycle copied as convention from the health governance |
AI.Support.Gates |
gate records and packets, decisions, grants (scope, binding to artefact hash, expiry), role checks against Authority claims, held batches | IGateService, IGrantService, IHeldBatchService |
Persistence, Ledger | Trust and Data § 2–3, Gating § 1 |
AI.Support.Skills |
procedure and tool execution: resolve pinned source/binary packages, run bound segments, validate outputs/assertions and record evidence; declared connector effects retain dry-run/reversal | ISkillRunner, IToolPackageRunner, SkillManifest |
Contracts, Connectors.Abstractions (injected execution/catalogue ports) | Agents Memory and Skills |
4. Libraries — copied and linked
| Set | Libraries | Why here | Source |
|---|---|---|---|
| Copied | AI.Contracts (canonical JSON, hashes, seals), Workflow.ServiceActivity.Contracts, AI.Activity.Abstractions, AI.Abstractions, AI.AzureOpenAI, RabbitMQ.Client, RabbitMQ.Topology, RabbitMQ.Integration, RabbitMQ.Outbox — RabbitMQ.Client/.Topology exist on master/bulstrad-qa and on the health branch; .Integration/.Outbox only on the branch; the branch versions are copied and owned; nothing is linked (D48; T2 GIT-6), Workflow.StateMachines (generic), Manager pieces (ClosedCapabilities, options + readiness check, plugin registry + assembly preload, governance DTOs) |
small, decoupled, owned by us afterwards | Software Architecture § 9.1 |
| Linked from the estate | Microservice.Rpc, .RpcCaller, Extensions.RabbitMQ, Workflow.Tasks, Authority.Plugins.Base (+ DBModels.Oidc), Ablera.Serdica.Plugin, Microservice.Consumer, Microservice.Initializer(.EndpointsRegistration), DBModels.Serdica, DbConfig, Scheduling(.Plugins.Oracle), Extensions.Serilog, HealthChecks, McpClient |
present on master/bulstrad-qa |
Software Architecture § 9.3 |
5. Plug-ins
| Plug-in | Holds | Rule |
|---|---|---|
AI.Support.Plugin.Bulstrad |
the customer-specific skills (Bulstrad connectors' scopes, BSO rules, Bulstrad reply register, product-family checklists), the prompt catalogue seed, connector scope rows | customer specifics live only here (Architecture § 1); loaded through Ablera.Serdica.Plugin with PluginsPrefix "Ablera.Serdica.AI.Support"; ships disabled, enabled per environment |
AI.Support.Plugin.Eval |
golden corpora and replay fixtures per module | never deployed to the QA instance; used by the developer instance and CI |
6. Data
| Store | What | Where |
|---|---|---|
SRD_SUPPORT |
the table set of Architecture § 3.1: work, planning, decisions, evidence, ledger, registry, queues | QA IPAL Oracle, target user SRD_SUPPORT (not yet created — verified 03.09.2026; D30/D47) |
| Papers volume | working papers, artefacts, compaction summaries, tool-result spills | QA host volume behind the Papers service; backed up with the host |
| Memory store | article bodies and AGENTS.md files; a git repository the platform owns (history = versions) with metadata in SRD_SUPPORT |
QA host volume; mirrored to GitLab |
| PL/SQL mirror | one commit per detected package change | a GitLab repository the platform owns alone |
| Target systems | IPAL / INSIS / ABACUS per environment, Jira, M365, HDesk, GitLab, Kibana, BI Publisher | reached only through connectors under grants |
7. Cross-cutting
| Concern | Implementation |
|---|---|
| Authentication / roles | Authority JWT (Authority.Plugins.Base); roles are delivered by the platform, not by a directory (D134): the six platform roles are rows of the estate's SRD_SYS.LT_USER_ROLES, assigned per user in SRD_SYS.USER_ROLES, and Authority issues one role claim per row in the token — the platform's endpoints are gated the way every Serdica endpoint is, by SRD_SYS.Routes.AllowedRoles (Trust and Data § 3, D97/D134) |
| Configuration | appsettings.json per branch, SERDICA_* overrides, Vault secrets; everything disabled by default |
| Observability | Serilog → fluentd → Kibana; OpenTelemetry spans per turn and tool call; metrics from the ledger, not from counters |
| Realtime | SignalR on the estate topology (hub + one-use Redis ticket + HMAC group alias + Rabbit fan-out + outbox) |
| Messaging | in-process typed messages inside a worker; Rabbit lanes (RabbitMQ.Integration consumers, Outbox publishing) across workers and to peer services |
| Testing | unit tests per library; replay tests on recorded sessions (no API keys); eval sets per profile; the T1 experiment (Agent Runtime § 10) |
| Deployment | one Dockerfile, one generated CI file, one compose unit; the write executor applies DDL under HW-ddl with the current D65 stage identity (D104; Software Architecture § 10.2) |
7.1 What the provider seam can normalise, and what it cannot P
The route seam makes a provider swappable; it does not make providers equivalent, and pretending otherwise is how a working case regresses on a different route. Stated so the limits are designed for rather than discovered:
| Concern | What the seam does |
|---|---|
| Chat and tool calls | Normalised. Every target provider exposes the same shape — a declaration, a call, a result — and the adapter maps it to one internal form |
| Structured output | Normalised, with a fallback. Where a provider carries a response schema natively it is used; where it does not, the seam validates against the same schema and re-asks on failure |
| Context budget | Not normalised — context windows differ by an order of magnitude. Intake and compaction budget to a common size so a case behaves the same on every route, rather than to whatever the current route allows |
| Prompt caching | Provider-specific, and worth the special-casing. The knowledge and profile prefix is identical on every turn and is the bulk of the tokens; the adapter that cannot cache it is priced accordingly, and the ledger shows it |
| Quality | Not normalisable. A cheaper route models a tariff worse, and the failure is quiet — a plausible model rather than an error. So the ledger records which route produced which case, making a regression attributable, and a profile's eval set runs against each route before that route is offered for that profile |
⚠ The last row is the one that turns into an incident. A route change is a behaviour change, so it is a governed decision per profile (Agents § 0.3), not a deployment convenience.
8. Dependency rules P
P This is the compile-time dependency graph. Responsibilities in § 3 are logical collaborations, not ProjectReference edges. AI.Support.Contracts is the small owned library for case/task DTOs, unit-of-work/store ports, catalogue readers, execution/evaluation ports and notifications. Copied AI.Contracts supplies canonical JSON, hashes and scalar primitives.
| Project or group | May reference |
|---|---|
| AI.Contracts, copied AI.Abstractions | their own small dependencies; no platform service |
| AI.Support.Contracts | AI.Contracts and BCL |
| Connectors.Abstractions | AI.Support.Contracts and AI.Contracts |
| Persistence | AI.Support.Contracts, EF/Oracle libraries |
| Sessions, Ledger, Papers, Memory, Gates | AI.Support.Contracts and required client libraries; collaborate through ports |
| Skills | AI.Support.Contracts, Connectors.Abstractions; execution is an injected port, not a Runtime reference |
| Providers | AI.Support.Contracts, AI.Abstractions and the selected adapter |
| Runtime | AI.Support.Contracts, Connectors.Abstractions, AI.Abstractions; injected catalogues/stores/tool handlers |
| Governance | AI.Support.Contracts; IEvaluationExecutor is implemented by Runtime |
| Connector implementation | Connectors.Abstractions and its client SDK; case/task ids are opaque correlation, not workflow knowledge |
| App host and customer plug-ins | compose/register implementations; plug-ins supply customer seeds and handler bindings |
P The host opens a unit of work for each platform transition and passes it through store/ledger/outbox ports. No store commits behind its caller. A scoped DbContext can implement that unit of work, but neither context nor transaction crosses a model call, target call or human wait.
P AgentExecutionContext exposes an explicit filtered tool/reader facade. Standard .NET child scopes share registrations; they are not capability subtraction. A model receives neither IServiceProvider nor credentials. The connector resolves credentials from a persisted execution permit and rechecks scope. See Runtime § 3.
P Customer table names, endpoint conventions and source layouts are plug-in/shape data, not runtime switches. Closed protocol states are versioned software contracts; business case types, stage outcomes, profiles and policies remain data. Copied projects are owned here; no ProjectReference points into the unmerged health module.
9. Reuse — the map and the verdicts
| Platform need | Reused asset (branch) | How |
|---|---|---|
| Model access | Ablera.Serdica.AI.{Abstractions,OpenAI,AzureOpenAI,AzureDocIntel} |
provider adapters behind interfaces = the model-route seam per agent profile |
| Skill lifecycle | AI.Prompting + Manager Governance (AiGovernanceService, AiPromptTestService, PromptRuntimePublicationService) + 006-factory-prompt-catalogue.sql |
skills are governed like prompts: catalogued in DB, tested before publication, published to the runtime; customer packs as plugins. F Lifecycle EDITING → LINTED → EVALUATED → APPROVAL_PENDING → APPROVED → ACTIVE → RETIRED; activation demands both a PASSED evaluation and an APPROVED approval; published versions are immutable by Oracle trigger; rollback is re-activating a retired version, audited as ROLLBACK (verified 01.09.2026). ⚠ ownership does not exist on the branch — see Agent Framework § 6.2 |
| Audit + cost | AI.Metering + Persistence AiCostLedgerStore, AiProviderAttemptStore |
the tool-parsable audit/cost ledger (Metrics PG-6) extends this exact pattern |
| DB pattern | AI.Persistence: versioned DbScripts/SrdAi/001…006, install-clean/upgrade/grant-runtime/verify-*, AiRetentionPolicyEvaluator, EF AiDbContext |
SRD_SUPPORT follows it verbatim: baseline → numbered upgrades → grants → verify scripts → retention |
| Delegation | Workflow.ServiceActivity.Contracts (AiActivityRequest/Completion/Cancel/RevisionsV1, WorkflowExecutionAuthorization) + Manager Runtime/Activity*Service, AiActivityOutboxPublisher, RabbitMQ {Client,Integration,Outbox,Topology,Rpc} |
the cross-service work-unit shape: request → acceptance → continuation → terminal commit, with outbox delivery |
| Deterministic lifecycles | Workflow.StateMachines (pure, no persistence/clock) |
case/task/gate state transitions |
| Plugin registry | Manager AiActivityPluginRegistry |
the profile/skill/connector registry loader |
| Papers/attachments | a dedicated token-gated file service owned by the AI.Support module |
F FileServer.V2 objects are immutable by schema (IMMUTABLE_FLAG default 1, no version chain); a keyed re-save with changed bytes returns 409, scope codes are a closed Oracle CHECK set, tags are a closed vocabulary, listing is by scope + key-prefix only (never by owner), delete is soft with a daily sweeper, every write is a synchronous filesystem + Oracle-BLOB dual write, and text/markdown is rejected by media-type detection (verified 01.09.2026). Rewriting one named paper many times per case is exactly what it refuses — so all case papers and final revisions stay in the dedicated Papers service; no second FileServer publication is assumed |
| Secrets/config | root AGENTS.md toolchain: make prepare_files (Vault → .env/compose), branch-differentiated appsettings.json |
secrets never in the repo; ⚠ appsettings are per-environment-branch (memory) |
| Test workspaces | local-setup/health-ai-{local-fullflow,e2e,qa-connected} scenarios |
the eval/scenario workspace pattern |
Name, source, or nothing — per project (decision 02.09.2026): the branch is not merged and nothing is linked from it. Contracts, abstractions, the Azure OpenAI adapter, the Rabbit integration/outbox libraries and a handful of Manager classes are copied into the module; Persistence, Metering, Prompting, the governance and runtime services and the host are name-only (their conventions re-implemented over SRD_SUPPORT); the Workflow/StellaOps tree, File Server V2 and the health plug-ins are nothing; Microservice.Rpc, Workflow.Tasks, Authority and the host plumbing are linked from the estate's own master/bulstrad-qa, where they exist; RabbitMQ.Client/.Topology are copied from the health branch and owned (D48; T2 GIT-6). The full table with evidence: § 9. ⚠ bulstrad-staging and bulstrad-prod run the old source layout without any of those libraries — which is why the platform is deployed on QA and reaches the other environments through connectors (Software Architecture § 10.1).
F Placement is not blocked (verified 01.09.2026). D-P2 — "Ablera.Serdica.AI.Manager is the only Serdica AI runtime deployable" (00-program.md:18) — is prose, not enforcement: src/Serdica/Ablera.Serdica.AI has zero files on master, bulstrad-qa, bulstrad-staging and bulstrad-prod, existing only on the unmerged health branch, and the one code-level guard asserts a single deployable inside that module's own tree, which a sibling in src/Serdica/ does not touch. § 1's layout stands. P The wording is accepted as-is (Vladimir owns the health-AI program — D27-a, D41 of 02.09.2026); a one-line rewording — "one AI runtime host per module; AI.Manager is the health module's" — is queued for the next time the health branch is touched. No rename; the AI_MANAGER wire vocabulary stays.
The health-AI branch codex/health-ai-csharp-architecture will not be merged (decision 02.09.2026). Nothing is linked from it. For every project the verdict is one of three: name-only (take the naming and the convention, write our own), copy-source (copy the files into the AI.Support module and own them), or nothing. Projects that exist on master/bulstrad-qa are linked from the estate itself, not from the branch. Inventory taken 02.09.2026 by read-only git over the branch and the four estate branches; every row names its evidence.
P This inventory is the selection, not a survey (decision Vladimir, 02.09.2026 — D62). The platform needs the capabilities in § 1–3 regardless of where they come from — the question is only which source serves each one best, and each row below is that judgment, made now, with its evidence attached. Rules of the selection: a copied library is ours from the day it lands — versioned with the module, no upstream tracking, no ProjectReference back into the branch; a name-only capability is re-implemented over SRD_SUPPORT to the stated convention; a linked library is taken from the estate's own master/bulstrad-qa line, never from the branch. Adding a library later means adding a row here with the same three-way verdict and evidence — not a reference appearing in a .csproj first.
9.0 The fact that shapes every verdict — two source layouts F
| Lineage | Branches | Layout | Has src/Serdica/, src/__Libraries/, Authority, McpProxy |
|---|---|---|---|
| new | master, bulstrad-qa, the health-AI branch |
src/Serdica/<Svc>/, src/__Libraries/, CI generated by tools/Ablera.Serdica.CiJobsBuilder |
yes |
| old | bulstrad-staging, bulstrad-prod |
src/<Area>/<Svc>/ (e.g. src/Core/Plugins/Core.Plugin.Bulstrad, src/BusinessInsurance/PasPolicy/; verified on origin/bulstrad-staging 04.09.2026), shared libraries as NuGet packages from src/Common/CommonCustomLibraries/, src/IdentityServer/ instead of Authority |
no — zero matches for Ablera.Serdica.Authority or McpProxy on either |
Consequence: "already in the estate" means the QA line. The platform's development target is Bulstrad QA (Software Architecture), so every link-as-is verdict below holds there; the customer-facing STAGING/PROD lineage has none of the new-layout libraries. The platform does not need to be deployed on STAGING or PROD to work their tickets — it needs connector reach to them (§ 10.3) — so the split is a constraint on where the platform runs, not on what it can touch.
9.1 Copy-source — small, decoupled, and we own it afterwards P
| Project (branch path) | .cs | Depends on | Why copy |
|---|---|---|---|
Ablera.Serdica.AI.Contracts |
23 | JsonSchema.Net, jsoncanonicalizer | root of the contract graph: canonical JSON (SERDICA-JCS-1), scalars, hashes, seals — the ledger's canonicalisation (Architecture § 3.2); zero coupling; its tests travel with it |
Ablera.Serdica.Workflow.ServiceActivity.Contracts |
13 | AI.Contracts | the cross-service work-unit shape (request → acceptance → continuation → terminal commit) used for peer agents |
Ablera.Serdica.AI.Activity.Abstractions |
1 | the two above | the one-handler-per-activity plug-in boundary |
Ablera.Serdica.AI/__Libraries/Ablera.Serdica.AI.Abstractions |
3 | — | the provider-neutral request/result/usage seam; forbids provider SDKs, Oracle, Rabbit, web |
Ablera.Serdica.AI/__Libraries/Ablera.Serdica.AI.AzureOpenAI |
10 | AI.Abstractions, Azure.Identity, OpenAI | the production Azure OpenAI adapter — Responses endpoint, deployment-name routing, Entra first / key fallback, store=false, strict output schema. P copied and owned — the platform's single provider abstraction (D39, Agent Runtime § 9.3); no framework dependency for it |
Ablera.Serdica.Workflow.StateMachines — the generic WorkflowExecutionStateMachine only |
3 → 1 | — | pure state transitions, no persistence or clock; drop the Claims machine |
__Libraries/Ablera.Serdica.RabbitMQ.Integration (minus the Health-AI lane catalogue) |
7 | RabbitMQ.Client, .Topology | durable manual-ack consumers, closed dispositions, prefetch/drain, delayed retry, DLX/parking |
__Libraries/Ablera.Serdica.RabbitMQ.Outbox |
3 | RabbitMQ.Client | store-agnostic outbox leasing + confirmed publishing; our SRD_SUPPORT supplies the rows |
RabbitMQ.Client and RabbitMQ.Topology — the branch's versions, whole (supervisor, confirmed publisher, strict topology validator; +626/−6 over master, source-compatible) |
7 + 7 | Extensions.RabbitMQ | used as libraries (decision Vladimir, 02.09.2026 — D48): the two projects are copied into the module's __Libraries and referenced there; master's older versions are not linked, so the module carries one Rabbit stack, not two |
Manager pieces: ClosedCapabilities.cs (fail-closed null objects), AiManagerOptions + readiness health check, AiActivityPluginRegistry + the shared-contract-assembly preload (Program.cs:11-27), AiGovernanceContracts.cs (37 DTOs — the shape of a prompt/route governance API) |
~6 files | Activity.Abstractions, Ablera.Serdica.Plugin | generic, no persistence coupling |
Rabbit-lane hosted service pattern (AiManagerRabbitLanesHostedService, AiExecute/CancelIntegrationHandler) |
3 | RabbitMQ.Integration | copy as a pattern after stripping the Persistence calls |
__Libraries/Ablera.Serdica.DocumentProcessing |
6 | SkiaSharp, PdfPig, LibTiff, LibHeif | only if intake needs PDF/image derivatives (S1 tariff PDFs do) — heavy native packages; otherwise nothing |
Es256PayloadSealSigner |
1 | — | if the ledger's periodic seal is signed (Architecture § 3.2) |
9.2 Name-only — the convention is ours to re-implement P
| Project / class | What is taken | Why not the code |
|---|---|---|
__Libraries/Ablera.Serdica.AI.Persistence |
the DbScripts/<Schema>/NNN-<change>.sql layout + install-clean.sql / upgrade.sql / grant-runtime.sql / acceptance-*.sql / verify-*.sql; target user SRD_SUPPORT (not yet created — verified 03.09.2026; the owner/runtime split is not carried — D47), connection string as a Vault secret DB_URL_AI_SUPPORT; immutability triggers (002-immutability-and-payload-guards.sql); twice-runnable idempotency |
SRD_AI hard-coded in 390 places, compiled EF model for another schema |
__Libraries/Ablera.Serdica.AI.Metering |
the rule set: provider intent + price pins before a paid call, cost only from pinned catalogue rows | depends on Persistence |
__Libraries/Ablera.Serdica.AI.Prompting |
immutable prompt binding, versioned components + {{variable}} placeholders, untrusted text appended last |
depends on Persistence |
AiGovernanceService, AiPromptTestService, PromptRuntimePublicationService |
the lifecycle EDITING → LINTED → EVALUATED → APPROVAL_PENDING → APPROVED → ACTIVE → RETIRED, activation needs PASSED evaluation + APPROVED approval, rollback = re-activate a retired version, publication hash-verified (Agent Framework § 6.1) |
import Persistence + EF in 3 of 4 files |
GovernedProviderRouteSelector, GovernedAiRuntimeSelector, AiModelCatalogueEntry.DataZoneCode |
versioned routes from DB rows, ProviderCode constrained in code, residency recorded per model option — the seam where the module data boundary is enforced (Agents § 0.3) |
Persistence + Metering + Prompting coupling |
Runtime acceptance/fencing (FencedActivityExecutor, ActivityRuntimeIdentity, ActivityTerminalCommitService, ActivityContinuationService) and cancellation (ActivityCancellationService, TruthfulAiProviderCancellation) |
fence every write by execution epoch, lease owner and row version; a stale handler fails closed; cancel is truthful about what the provider did | 18 of 39 Runtime files import Persistence |
AiScheduledJob coordinator over Ablera.Serdica.Scheduling |
the maintenance-job shape: capability probe, reconciliation, retention sweep, compaction | Persistence |
Ablera.Serdica.AI.Manager host, appsettings.json conventions |
PluginsConfig.PluginsPrefix, PluginsOrder, everything ships disabled (AiRuntime.AcceptActivities: false) and is enabled per environment |
it is the health host; ours is Ablera.Serdica.AI.Support.Webservice |
Ablera.Serdica.HealthRecognition.Contracts, …Plugin.BulstradClaimsHealthPromptCatalog, …Plugin.ConsumerMigration |
the <Domain>.Contracts and <Host>.Plugin.<Kind>.<Impl> naming; the "prompt catalogue as a plug-in" idea |
health-domain content |
FileServer.V2.Persistence |
the same DbScripts layout, second confirmation of the pattern | SRD_FSV2 |
9.3 Link as-is — present in the estate on the QA line F
Project (src/__Libraries/… unless noted) |
master | qa | staging | Note |
|---|---|---|---|---|
Ablera.Serdica.Microservice.Rpc, .RpcCaller |
yes | yes | no | command destinations from SRD_SYS endpoint metadata, bounded request/reply; the branch changes 16 lines — review, do not link the branch copy |
Ablera.Serdica.RabbitMQ.Client, .Topology |
yes | yes | no | not linked — the branch's versions are copied as libraries (§ 1, D48) |
Ablera.Serdica.Extensions.RabbitMQ |
yes | yes | yes (src/Common/CommonCustomLibraries/) |
the only Rabbit library present on all lineages |
RabbitMQSerdica |
yes | yes | yes | oldest wrapper, single never-recreated channel (topology) — avoid unless forced |
Ablera.Serdica.Workflow.Tasks |
yes | yes | no | starts Core (Camunda) processes over Rabbit — the S5 process-registration connector's seed |
Ablera.Serdica.Authority + DBModels.Oidc(.Migrations) + Authority.Plugins.Base + Plugin.{Ldap,Bulstrad,Standard} + Plugins.LdapUtilities |
yes | yes | no (STAGING/PROD run src/IdentityServer/) |
the QA identity provider; a new service validates tokens with Authority.Plugins.Base (JwtBearer). Byte-identical branch ↔ master |
src/Serdica/Ablera.Serdica.FileServer (legacy) |
yes | yes | yes (other path) | the deployed /files/ service; not used for papers (Agent Runtime § 11.2) |
Ablera.Serdica.McpClient (+ McpProxy endpoint/envelope pattern) |
yes | yes | no | the MCP HTTP client; copy the BaseToolResponse<T> envelope idea; the Mongo/Bethos parts are IntentGPT-era and are not carried |
Ablera.Serdica.Plugin, Microservice.Consumer, Microservice.Initializer(.EndpointsRegistration), DBModels.Serdica, DbConfig, Scheduling(.Plugins.Oracle), Extensions.Serilog, HealthChecks |
yes | yes | as NuGet | the host plumbing every new-layout service uses |
9.4 Nothing P
| What | Size | Why |
|---|---|---|
The whole Ablera.Serdica.Workflow / StellaOps tree (host, engine, data stores, signaling, renderers, plug-ins, tests) |
~1,100 files | a separate program; agents control their own jobs (Non-Goals N-4) |
Ablera.Serdica.FileServer.V2 (host, Core, Persistence, Pipeline, Storage plug-ins) |
~45 files | not deployed anywhere; papers use the module's own token-gated file service (Agent Runtime § 11.2) |
The four health plug-ins, HealthResultFinalization, Qdrant reference index, File Server V2 adapters in the Manager |
— | health-claims domain; Qdrant is not on STAGING/PROD |
__Libraries/Ablera.Serdica.AI.OpenAI |
2 | benchmark adapter, "must refuse production" |
__Libraries/Ablera.Serdica.AI.AzureDocIntel |
5 | only if OCR enters scope (it does not in v2's first slice) |
All test/* projects except AI.Contracts.Tests |
— | test their subjects, not ours |
9.5 What the branch never had F
No Dockerfile and no build/push CI job exist for Ablera.Serdica.AI.Manager or FileServer.V2 on the branch; only Workflow plug-ins gained CI, plus three test/doc jobs. The branch is 605 commits behind master (last commit 17.08.2026). Its infrastructure design (docs/architecture/health-ai/17-infrastructure.md) is nevertheless the best written description of the estate's deployment mechanics and is the source for Software Architecture § 10.2.
10. Hosting and operations
A platform that reads production tickets, queries production databases and writes to them under a gate has to run somewhere with some identity, reached over some network, operated by someone. None of that was written down before 02.09.2026. This page fixes what the repository and the estate's memory establish, decides what can be decided now, and names the questions only Bulstrad IT or Ablera DevOps can answer. Evidence base: the repository inventory of 02.09.2026 (§ 9), .gitlab-ci/* on bulstrad-staging and master, the health-AI infrastructure design (docs/architecture/health-ai/17-infrastructure.md on the unmerged branch), and the estate memories linked inline.
10.1 Where it runs P
P Operational responsibility. This is the production control service even though its host is named QA. API/worker, broker, Oracle metadata, papers and key service form its operational dependency set. Before real writes, prove backup/restore, off-database export, kill switch and reconciliation. A ledger export alone cannot restore paper bytes, keys or unknown target effects. The dated measurements below are the known baseline, not a current restore test.
The platform is one deployment, on the Bulstrad QA environment, and it works every environment from there. It does not get promoted to STAGING or PROD as a service; its connectors reach STAGING and PROD databases and systems over the network, exactly as v1 operators do today from laptops over VPN (Baseline § 1).
P The control plane's placement is itself configuration (decision Vladimir, 02.09.2026 — D63): which environment hosts the platform, and which environment a case starts from, are plug-in values (platform.host, source.start_flows — Whitelabel Catalogue § 4), never code. The Bulstrad defaults are below; a second customer names its own host and flows. Per case type — and, since D123, per product line, for the Configuration module's working environment as much as for the Development module's start — the environment differs by design: new, unreleased products (the health line) start on QA, their work is backported to master, and their production target is the planned bulstrad-prod-2; running products start on bulstrad-staging and the fix then travels to master and bulstrad-qa (Development Details DG-2).
| Component | Where | Evidence / rule |
|---|---|---|
Ablera.Serdica.AI.Support.Webservice + worker replicas |
a new compose unit serdica-ai-support on the QA compose host serdicaqa.bulstrad.bg = 10.239.82.105 (also RabbitMQ) |
F templates/variables.yml@bulstrad-staging:30 BULSTRAD_QA_ENV_MACHINE_ADDR (QA broker memory); runtime model is docker compose on VMs — SERDICA_RUNTIME compose-vm, docker compose -p serdica-<env>, vars.<env>.j2, nginx reverse proxy (AGENTS.md@master:336, 17-infrastructure.md); no helm, k8s or swarm file exists on any branch |
target schema SRD_SUPPORT (not yet created — verified 03.09.2026; D30/D47) |
QA IPAL Oracle db.serdicaqa.bulstrad.bg = 10.239.82.106 (office/VPN) / 10.8.88.74 (container-internal), SID orcl1 |
P decision 02.09.2026 (Architecture § 1); F the container address is confirmed by variables.yml:31 — it was "to confirm" in the dual-address memory |
| Papers / artefact store | the module's own token-gated file service, storage on the QA host volume, indexed in SRD_SUPPORT.PAPERS |
Agent Runtime § 11.2 |
RabbitMQ lanes ai_support.request, cancel lane, event fan-out |
the QA broker on 10.239.82.105 (management :15672; RabbitMQ 3.10.23, flagged end-of-life in 17-infrastructure.md:22) |
F QA broker access |
| Realtime to the UI | SignalR on the same host behind nginx /ai-support/ |
UI § 3 |
| serdica-ui administration section | the QA serdica-ui deployment | the platform's UI is part of the QA UI build |
| Logs | stdout → fluentd → Elasticsearch data stream fluentd.bst-qa-env → non-PROD Kibana http://10.239.82.110 |
F Kibana access; filter by container_name.keyword, correlate by fields.RequestId |
| Traces / metrics | OpenTelemetry from the runtime (Agent Runtime § 8); P the estate has no OTel collector today — the first target is the same Elasticsearch, or a collector added to the compose unit. P Prometheus is the named candidate for the metrics sink (stated 03.09.2026), beside the existing Elasticsearch for logs; the choice is an operations decision, not an architecture one, because the ledger — not the sink — is where the platform's own metrics come from (Metrics) |
Why QA and not Ablera DEV: the PL/SQL developer works on QA, the customer's branches are bulstrad-*, the product data is the customer's, and the identity provider there is Authority, which the platform reuses. Why not STAGING or PROD: those run the old source layout without src/__Libraries, Authority or McpProxy (Software Architecture § 9.0); deploying the platform there would mean packaging every library as NuGet for a lineage that is about to receive the new layout anyway. The platform does not need to live there to work there.
10.2 How it is built and deployed F
The estate's mechanism, unchanged:
- Build — GitLab CI on a
shell-dockerrunner runsdocker buildx buildper service from a Dockerfile atsrc/Serdica/<Module>/<Service>.Dockerfile, tags${CI_REGISTRY_IMAGE}/<service>:<branch>_TEMP-<pipeline>intoregistry.ablera.dev(templates/build-job.yml@bulstrad-staging:14-78). The CI file per service is generated bytools/Ablera.Serdica.CiJobsBuilderon the new layout (.gitlab-ci/autogenerated/microservices/*.yml@master); P its invocation has no README — readProgram.csbefore generating. - Push — on a
bulstrad-*branch the image is re-taggedbulstrad-registry.ablera.dev/<service>:<branch>(templates/push-job.yml:14-51), so the tag QA pulls isbulstrad-qa. - Deploy — a manual job runs the
registry.ablera.dev/ablera/serdica-infrastructure:1.0.0Ansible image over SSH to the VM with Vault secrets (templates/deploy-job.yml:1-31). The playbooks,docker-compose.<unit>.ymlfiles,vars.<env>.j2and nginx templates live in the separateserdica-infrastructurerepository; the Bulstrad deploy jobs are not in serdica-backend — P the trigger is a rule for the deployment agent (D130): the last build job ofserdica-infrastructure, then the deployment-phase job that includes the service (the.ymlfiles name it);bulstrad-stagingandbulstrad-prodcome from thev3.0.0branch with their own compose files,masterand the rest frommaster. - Configure — per-branch
appsettings.jsonoverridden bySERDICA_*environment variables; secrets come from HashiCorp Vault (vault.ablera.dev) at deploy time only, containers never seeVAULT_TOKEN(17-infrastructure.md:96; branch memory). - Register — the service declares
MicroserviceConfig(exchangeai_support, queueai_support.request) and writes itsSRD_SYS."EndpointSections"rows at start-up throughMicroservice.Initializer.EndpointsRegistration(service topology). - DDL —
DbScripts/SrdSupport/NNN-*.sql+install-clean/upgrade/grant-runtime; the write executor applies it under theHW-ddlgate with the identity of the current D65 stage (D104).
P What the module adds to the repository: the project tree of Software Architecture § 0, one Dockerfile, the generated CI file listing every src/__Libraries/* dependency, appsettings.json per branch with everything disabled by default (the health-AI convention: AcceptActivities: false until the environment enables it), and the DbScripts. What it adds to serdica-infrastructure: the compose unit, vars.<env>.j2 entries, Vault secrets (DB_URL_AI_SUPPORT, provider keys, connector credentials), a deploy_serdica_ai_support job, an nginx location.
10.3 Network reach — the connectors' world P
| From the QA host to | Address | Needed for | Status |
|---|---|---|---|
| QA IPAL / INSIS | 10.8.88.74 · 10.8.88.48 (in-env) |
Configuration working environment for the health line (per product line — D123), Development module (id source) reproductions for new products, target SRD_SUPPORT |
verified in-env, works today (dual-address memory); SRD_SUPPORT not yet created (DB-7) |
| STAGING IPAL / INSIS | 10.239.82.122 · 10.239.82.123:1522 |
Development start for running products (source.start_flows, D63); Support cross-checks |
configured default; unverified — probe pending; direct or unreachable, the answer is a reach row (D46, D132) |
| PROD IPAL / INSIS | 10.239.82.109 (10.8.90.17 in-env) · 10.239.82.103 |
Support — the default environment for tickets | configured default; unverified — probe pending; the row records whether the QA host reaches PROD directly; if not, the route is a network request to the estate, not a hop (D132) |
| ABACUS gateway per environment | per env | getRates skill, pricing investigation | configured default; unverified per environment — as the matching reach row above |
Jira ablera.atlassian.net, Microsoft 365 (shared mailbox), HDesk hdesk.bulstrad.bg, GitLab gitlab.ablera.dev, Kibana 10.239.82.110, BI Publisher |
internet + Ablera network + Bulstrad network | intake and evidence connectors | configured default; unverified — egress unknown; where closed, the egress rule is the request to the estate (D132) |
| Azure OpenAI, EU region | internet | every model call of the Support module (SG-9) | configured default; unverified — probe pending; direct egress from the QA host to the provider's EU endpoint is the deployment assumption (D132) |
P Reach is a platform capability, not a network request (decision Vladimir, 02.09.2026 — D46), and every connection is direct (decision Vladimir, 04.09.2026 — D132): the platform brings a probe, not a tunnel. Each target's reach — direct or unreachable from this deployment — is data per environment in CONNECTOR_SCOPES rows of kind reach (target {host, port, paths}, route {kind: direct|unreachable}, modes, credential_ref, health, verified; unverified addresses such as 10.239.82.108/.101 are refused for writes — Challenge Rounds § R2 § 9). The v1 paths already exist as direct paths: TNS to the databases, HTTPS to Jira/Graph/HDesk/GitLab, SSH to the Linux hosts; the v1 SSH tunnel through a Bulstrad server to the SOA host (BI Publisher, ЕИСОУКР, GetKATData — memory) is not carried over — those targets are reached directly or the case parks on a capability gap and the missing route becomes a specific, defendable request to the estate's network (D128), whose answer changes a row. Model calls go directly from the QA host to the provider's EU endpoint; there is no Ablera-side model gateway in the design. The remaining request to Bulstrad IT is therefore network only — the roles need no directory change (D134, § 10.4).
10.4 Identities and service accounts P
The PC agent's named gap — the write path ran under a personal session (Product Configurator § P) — is closed here, or the platform is v1 with a web page.
| System | Identity | Rule |
|---|---|---|
SRD_SUPPORT |
one target user, SRD_SUPPORT (not yet created — verified 03.09.2026), owner and runtime alike; connection string as a Vault secret |
D47 creates the user; after that, estate DDL is applied by the write executor under HW-ddl with the current D65 stage identity (D104) |
| IPAL databases, per environment | read: a dedicated read-only account AISA_RO per environment (SELECT on the schemas the connectors declare); write: the existing privileged support identity the operators use today, wrapped by the connector and reachable only through a grant (Trust and Data § 2) |
one account per environment, never a shared one across environments — the grant's environment dimension must be a real credential boundary |
| INSIS databases | ABLERA_SUPPORT (writes, rule) and ABC_ACCESS (pricing) as today — stage 1 of the write-identity path (D65); the target is a dedicated least-privilege platform identity per environment under unified audit |
F SESSIONS_PER_USER = 6 → ORA-02391 (caps); the connector's session budget is that cap minus the humans' sessions |
| Jira | the existing shared AISA account (Internal Tag names the person; "AISA" tag appended) — a service account with an API token in Vault; the human on whose behalf a write happens is named in the comment body and the ledger | v1 convention kept (rule) |
| Microsoft 365 | an app registration restricted to the shared mailbox: Mail.Read for intake, and Mail.Send only where sending is enabled and authorised; connector audience checks still apply | replaces the personal delegated login of the v1 CLI |
| HDesk (OTRS) | the service credentials the v1 CLI uses, moved to Vault | — |
| GitLab | a project access token per repository, worktrees on the QA host volume | Development module (id source) |
| Authority (users) | operators authenticate against Authority (QA) → LDAP/AD for the password; the roles come from the platform — Viewer / Operator / Approver / Prompt publisher / Administrator / Customer representative are six rows of SRD_SYS.LT_USER_ROLES, assigned per user in SRD_SYS.USER_ROLES (or carried as the directory's bstRole attribute where the estate already does that), and Authority issues one role claim per assignment (Trust and Data § 3, D134) |
F Authority → Bulstrad_LDAP (OpenLDAP 10.250.10.140) first, Bulstrad_AD (10.8.40.17) failover (identity chain); F the role claims are built from USER_ROLES by Authority's UserAccountIdentityFacade.GetRolesClaimsAsync (Plugin.Standard, serdica-backend master) — Ablera owns Authority and the role rows, so no directory request is needed |
10.5 Who operates what [F → P]
| Concern | Owner today | For the platform |
|---|---|---|
| Build, push, Ansible deploy, container restarts | Ablera DevOps | unchanged; the platform is one more compose unit |
| DDL and grants on Bulstrad databases | write executor under HW-ddl after the one-time SRD_SUPPORT creation |
the write executor applies estate DDL with the current D65 stage identity (D104); SRD_SUPPORT is not yet created and QA backup work is prerequisite (DB-7) |
| Authority and its logs | Ablera | unchanged |
| LDAP/AD, OSB, INSIS, VMs, network, VPN | Bulstrad IT | network only: the direct routes the probe finds closed (D132); the platform roles are Serdica rows, not directory groups (D134) |
The serdica-infrastructure repository (compose units, vars.<env>.j2, playbooks) |
Ablera DevOps | read access plus separately scoped pipeline-trigger permission for the GitLab connector is a Development module prerequisite (Development Module § 5 G23): the deploy set of a change cannot be enumerated without seeing the compose units |
| The platform itself — operators, approvers, prompt publishers | new | the roles of Trust and Data § 3; the first operators are the v1 operators |
| Kill switch, canary activation, connector health, attachment scanning | new | Delivery § 4 — the kill switch is live control state checked at admission and every effect dispatch; a startup SERDICA_ variable supplies the default, not live propagation |
P The minimum alert set (D66) — the platform raises, into the estate's existing log alert channel (Kibana over fluentd.bst-qa-env): ledger seal/export lag beyond one interval (Architecture § 3.2) · connector health red per environment × system · budget hard-stops per case · gate escalation past its first interval (PG-12) · eval set red at publish · parked-case age past the case type's threshold · kill switch flipped (either mode). An alert is a ledger event first, so the operations view and the metrics read the same facts.
F QA has no working backup (measured 02.09.2026, Measurements § DB § 5–6): last RMAN job 26.07.2024, FAILED; NOARCHIVELOG; flashback off; no restore points; undo 900 s NOGUARANTEE; USERS tablespace 48 of 48 GB with 8.4 GB free; no SRD_SUPPORT user (verified 03.09.2026); unified auditing off and no statement audit options; 19c Enterprise Edition. STAGING is backed up nightly (also NOARCHIVELOG); PROD is ARCHIVELOG with completed backups. P The request to the Bulstrad DBA is therefore not "confirm SRD_SUPPORT is in the inventory" but: restore QA backups, switch QA to ARCHIVELOG, create the single SRD_SUPPORT user with its own autoextend tablespace and a profile with a SESSIONS_PER_USER cap (Ablera's DBA — D47), and enable a unified-audit policy on CREATE/ALTER PACKAGE for SRD_INTEGR. Until then the PL/SQL git mirror (Stage Planning § 3b) is the only durable copy of anything on QA, and the ledger's RPO on QA is undefined; all nfr.targets durability values are conditional on D45 restoring the QA backup path first.
10.6 Environments of the platform itself P
The platform has one production instance (on QA, as above) and a developer instance on an Ablera developer machine, run the way the health-AI rig runs qa-connected: the service local, connected over VPN to the QA database, broker and Redis. Eval sets and replay tests (Agent Runtime § 9.2 patterns 18–19) run on the developer instance and in CI against recorded sessions; nothing in CI touches a Bulstrad database.
10.7 Actions with owners P
Decided 02.09.2026 (D45–D48); listed here so the text is not lost and the owner is named.
| # | Owner | Action | Until done |
|---|---|---|---|
| 1 | Ablera (Serdica configuration) | Add the six platform roles as rows of SRD_SYS.LT_USER_ROLES (AISA_VIEWER, AISA_OPERATOR, AISA_APPROVER, AISA_PROMPT_PUBLISHER, AISA_ADMIN, AISA_CUSTOMER_REP), register the platform as an OIDC client of Authority (oidc-settings.json / SRD_SYS.OIDC_APPLICATIONS, scope roles), set SRD_SYS.Routes.AllowedRoles for the platform's endpoints, assign the first operators in USER_ROLES (D134). No Bulstrad IT directory request. |
roles are held as rows in SRD_SUPPORT.ROLE_ASSIGNMENTS with source = platform_row |
| 2 | Ablera dev / DBA | Restore QA backups and switch QA to ARCHIVELOG; create the single SRD_SUPPORT user with an own autoextend tablespace and a SESSIONS_PER_USER cap; enable a unified-audit policy on CREATE/ALTER PACKAGE for SRD_INTEGR (D47; Measurements § DB-5–6). |
the 15-minute off-database export (D45) is the only durable copy |
| 3 | Ablera DevOps | The compose unit serdica-ai-support in serdica-infrastructure and a read token on that repository for the platform's GitLab connector. |
the deployment agent cannot resolve the job by rule (D130) and deploy requests stay by hand |
| 4 | Platform team | The reach probe from the QA host on first deployment; the result — direct or unreachable per target — written into CONNECTOR_SCOPES (D46, D132); every unreachable target becomes one specific network request to Bulstrad IT with host, port and purpose. |
— |
Challenges
- P Memory store P (Challenge Rounds § R2 § 5): git is the store, Oracle the projection. Repository
aisa-memoryon the QA host volume, mirrored to GitLab by push; one file per article with frontmatter{id, version, state, owner_profile, provenance[], verify{}}— states are commits;SRD_SUPPORT.MEMORY_ARTICLESis an index rebuildable from the repository (commit first, row second; reconciled at start). Reasons: articles are immutable and versioned, retrieval is measured over bodies, and QA has no valid backup, so the mirrored repository is the durable copy that Oracle CLOBs would not be. Confirmed when: wave 1: every row'sgit_sharesolves; a rebuild from frontmatter shows zero differences. - P Handles table P (same source):
CASE_HANDLESstays inSRD_SUPPORT(D47 — one schema) and the boundary is cryptographic:value_encis AES-GCM under a per-case data key wrapped by a Vault transit key, so Oracle, its backups and a DBA'sSELECT *hold ciphertext; re-identification decrypts inside the connector process with aREIDENTIFYledger event; destruction at the customer's closure deletes the rows and the key — crypto-shredding, so backup copies die too. Confirmed when: the data-boundary fixture: ten identifiers → zero plaintext outsideWRITE_LOG.statement. - P
RabbitMQ.Clientdelta — settled (D48): the branch'sRabbitMQ.Client,.Topology,.Integrationand.Outboxare copied as library projects into the module; whether master later adopts the same delta is the library owner's matter, not the platform's. - P Which targets are reachable directly from the QA host — resolved 04.09.2026 (D132, A-8): there is no tunnel and no
viahost — a target the probe cannot reach directly is a specific, defendable request to the customer's network (host, port, purpose) — never wildcard access — whose answer changesCONNECTOR_SCOPESrows, not code (D128, D46). P The platform's SSH account on the Linux hosts is the estate'sdeploymentuser (D129). - F Hygiene: the BI Publisher credentials were stored in plaintext in
knowledge/policy_print_recipe.md; redacted 02.09.2026 to a${BIP_PASSWORD}reference — the value belongs in Vault. - P The Bulstrad deploy trigger lives in
serdica-infrastructureand is resolved by rule (D130): the last build job of the project, then the deployment-phase job that includes the service;bulstrad-stagingandbulstrad-prodfrom thev3.0.0branch, the rest frommaster. The compose unit for the platform itself is still Ablera DevOps' to add. - P
10.239.82.108is both the PROD compose host (CI variables) and the OSB/SOA facade in service topology — one host or a stale record;10.239.82.101is "bulstrad-on-prem" in CI and the decommissioned old TEST in memory — whether on-prem is live is unknown. Neither affects the QA placement; both affect the connector address book. - P OpenTelemetry sink — none exists in the estate; first target the existing Elasticsearch or add a collector to the compose unit.
P Provider implementation detail. Model Execution records the inspected copy source and structured-action loop over IInferenceClient. The copied adapter is one inference call; native tool calling and durable orchestration are not assumed to exist in it.