A Unified Claim Agent
A design for a single rule-driven agent that knows every courier, client, and claim rule, watches claims as they are ingested, and drives each one all the way through to submission and credit.
The central finding: ClaimIt already contains all the machinery. What it lacks is a single brain to tie it together. This document maps what exists, names the problem precisely, and proposes the one component worth building.
The thesis
Today the rules a claim agent should know are smeared across five subsystems — code constants, environment variables, and three separate database config tables. The "agent" is really two different agents plus a fleet of per-courier workflows that each re-derive policy locally.
So the design goal is consolidation, not greenfield: introduce one policy resolver as the source of truth, keep Temporal as the durable execution substrate, and feed a thin dispatcher from ingestion and sweeps.
Three layers that already exist
Every claim-automation system decomposes into the same three layers. ClaimIt has all three — they are just not unified.
What backs each layer today
| Layer | Component | Where it lives |
|---|---|---|
| Knowledge | Courier rules (code) | CourierRegistry / CourierDefinition — Enabled, RunsOnSelfHosted, EmailHandledByWorkflow, AllowsPortalCreatedWorkflows |
| Knowledge | Per-brand rollout gates | *WorkflowConstants.EnabledCclIds (e.g. ColissimoWorkflowConstants) |
| Knowledge | Which LLM categories may act | AICommsClientConfigService — EnableCreditLLM, EnableRejectionLLM, … per courier × client |
| Knowledge | Allowed rejection reasons | EnabledRejectionReasonIds JSON |
| Knowledge | Bulk-doc eligibility | ClaimsAgentGate (remittance / claims_report / bulk_import) |
| Knowledge | Brand on/off switch | AICommsClientExclusionService (AgentEnabled / AutoEscalate / NoAction) |
| Trigger | Ingest → classify → dispatch | ClassifyLoop → ProcessEmailDispatcher → ProcessEmailWorkflow |
| Trigger | Time-based sweeps | Temporal schedules (UPS / Erin / stuck-New) |
| Execution | Per-claim state machine | ClaimLifecycleBase / ClaimLifecycle<TState> |
| Execution | Sanctioned transitions | ClaimTransition.To*() + ClaimStatuses.DatabaseIds |
| Execution | Submission (browser / API / email) | per-courier OnSubmissionAsync hook |
The end-to-end already works
Colissimo is the clean reference implementation — the per-claim Temporal submission layer built under TECH-923 by Trevor Daniel. A claim runs the full distance today without a human touching it:
ClaimTransition.To*() is the only sanctioned way to change status (never set ClaimStatusId directly), and each transition writes a tagged ClaimLogEntry. The status ids are canonical constants in ClaimStatuses.DatabaseIds.
The problem: no single brain
Here is the crux. The question "can this claim be auto-progressed right now?" has no single owner. Five subsystems each hold a slice, with different mechanisms and different storage:
So "the agent knows the rules" is not true today — five subsystems each know a slice. Onboarding a new brand means editing code constants and database rows and hoping the two agree. There is no place to answer the whole question at once, and no single audit trail for why a claim did or did not advance.
There are also two
ClaimTransition.csfiles (backend/SDK/ClaimTransition.csandbackend/SDK/Objects/ClaimTransition.cs). Confirming which is canonical is a prerequisite to building anything on top of the transition layer.
Target architecture
Introduce one new component — a ClaimPolicyResolver — and route every gate decision through it. Ingestion and sweeps feed a thin dispatcher; the dispatcher asks the resolver for a policy; the per-claim Temporal workflow carries it out.
The ClaimPolicy object
The resolver returns one immutable record answering the whole question at once:
record ClaimPolicy
{
bool IsLive; // AllowsEmail/PortalCreated + CclId ∈ EnabledCclIds
Cluster Cluster; // from RunsOnSelfHosted
SubmissionChannel Channel; // Browser | Api | Email | Batch
AgentMode AgentMode; // AgentEnabled | AutoEscalate | NoAction
IReadOnlySet<Category> AutoActionable; // from the Enable*LLM flags
IReadOnlySet<int> AllowedRejectReasons; // EnabledRejectionReasonIds
EvidenceRequirement Evidence; // PoP / PoC required?
TimeSpan? FilingWindow;
bool AllowAccepted; // [ALLOW_ACCEPTED]; Credited stays agent-blocked
}The key property is that it wraps the existing sources rather than replacing them. It reads CourierRegistry, the EnabledCclIds constants, AICommsClientConfigService, and AICommsClientExclusionService behind one interface. Every call site that today re-derives policy locally instead asks the resolver. That makes the change low-risk, incremental, and unit-testable in isolation.
Build vs. reuse
The scope is deliberately narrow. Only one box is genuinely new.
| Piece | Effort |
|---|---|
ClaimPolicyResolver + ClaimPolicy model |
Build — the real work, but a read-only facade over existing sources |
| Migrate call sites to ask the resolver | Refactor — mechanical, incremental, one courier at a time |
| Dispatcher, ingestion, classification | Reuse as-is |
| Per-claim lifecycle + transitions + submission | Reuse as-is |
Resolve the duplicate ClaimTransition.cs |
Cleanup — prerequisite hygiene |
The single highest-leverage move is the resolver. It turns "five subsystems each know a slice" into "the agent knows the rules," without rewriting the execution engine that already works.
Two things to decide first
The agent's authority ceiling is a policy decision, not a code detail. Today
Creditedis hard-blocked for agent auto-execution andAcceptedneeds an explicit[ALLOW_ACCEPTED]marker. A unified agent makes widening that easier — so the resolver should treat those ceilings as first-class, auditable fields, never incidental flags.
The code-vs-DB split exists for a reason.
EnabledCclIdsis deliberately a code gate — a rollout safety brake that configuration cannot override. If the resolver flattens everything into DB config, that brake is lost. The resolver should read both tiers but must not collapse them.
Summary
You are not building a claim agent from nothing. You already have the knowledge sources, the triggers, and a durable execution engine that carries claims to credit — the Colissimo path (TECH-923, Trevor Daniel) proves the full New-to-Credited distance is already achievable per courier. What is missing is the one component that lets the system answer "what are the rules for this claim, and what am I allowed to do?" in a single place. Build the ClaimPolicyResolver, route every gate through it, and the fleet of per-courier workflows becomes a single rule-driven agent.