Appearance
Domain Packs
CARC governs AI agents that make consequential decisions. The core platform (sessions, Agent Runs, evidence, policies, risk, review, replay) makes no assumption about which kind of decision it is governing. Each kind is a domain pack that registers everything domain-specific:
| A domain pack registers | Healthcare | Lending |
|---|---|---|
Roles, on top of the core requester, human_reviewer, and system | clinician, nurse, pharmacist, care_coordinator | loan_officer, underwriter |
| Task types, each with the roles allowed to request it | discharge_summary, ... | loan_application_review |
| A subject type and synthetic cases | patient | loan_applicant |
| Claim types and recommendation categories | diagnosis; medication | adverse_action_reason; approve, decline, credit_limit |
| Policies scoped to the domain | medication_requires_review, ... | decline_requires_review, ... |
| Risk weights per recommendation category | medication: 25 | decline: 15 |
| Reference agents and a managed-execution prompt builder | discharge-summary-agent | loan-review-agent |
List what is installed with GET /runtime/domains (SDK: runtime.listDomains()).
What the domain decides
- Which agents can register. An agent's
domainmust be an installed pack, and every entry inallowed_tasksmust be a task type that pack registered. Anything else is rejected with a 400. - Who can request a task. The request's
user_rolemust be in the task type'sallowed_roles. - Which policies apply. Core policies (
domain: null) apply to every run. A pack's policies apply only to runs whose task type that pack registered. - How risk is weighted. Recommendations in a category the domain weights add that weight to the runtime risk score.
The domain-neutral output shape
Every agent, in every domain, returns the same AgentOutput:
ts
interface AgentOutput {
identity: { agent_id; agent_version; model_provider; model_version; task_type };
generated_at: string;
narrative: string;
claims: Claim[]; // claim_type is an open string: core types plus the domain's own
recommendations: Recommendation[]; // grouped by category, optionally with a numeric value
required_sections: Record<string, boolean>; // e.g. { discharge_instructions: true }
}Parameterized policies
Policy conditions are domain-neutral. What makes a policy specific to a domain is its condition_params and its domain:
condition_type | condition_params | Triggers when |
|---|---|---|
recommendation_category_present | { category } | Any recommendation has that category |
recommendation_value_above | { category, threshold } | A recommendation of that category has a value above the threshold |
claim_type_unsupported | { claim_type } | A claim of that type has no supporting source |
required_section_missing | { section, task_types?, when_recommendation_category? } | The section is not true in required_sections (optionally only for some task types, or only when a recommendation of a category is present) |
unsupported_claim_present | {} | Any claim is unsupported |
risk_level_high | {} | The runtime risk level is high |
critical_unsafe_recommendation | {} | High risk and two or more unsupported claims |
Every PolicyResult on a Decision Context Record snapshots the condition_type and condition_params that were evaluated, so a record stays self-describing even if a policy later changes.
Installed domains
Healthcare
Clinical documentation and decision support. Subject type patient.
- Task types:
discharge_summary,diagnosis_support,medication_reconciliation,care_plan_summary,radiology_follow_up_recommendation - Policies:
medication_requires_review(anymedicationrecommendation requires review),diagnosis_requires_evidence(an unsupporteddiagnosisclaim requires review, risk weight 25),discharge_instructions_required(a discharge summary without thedischarge_instructionssection requires review, risk weight 20) - Agents:
discharge-summary-agent(active, in-process reference agent),radiology-follow-up-agent(draft), andopenai-discharge-summary-agent(managed execution, active only when OpenAI is configured)
Lending
Consumer credit decisions. Subject type loan_applicant. Subjects carry underwriting inputs only (purpose, requested amount, income, debt-to-income ratio, credit score, employment), never attributes a lender must not consider.
- Task type:
loan_application_review(roles:loan_officer,underwriter,system,requester) - Policies:
decline_requires_review: anydeclinerecommendation requires an underwriter's review.credit_limit_above_threshold_escalates: acredit_limitrecommendation with avalueabove 25,000 requires review (risk weight 20).adverse_action_reasons_required: adeclinewithout theadverse_action_reasonssection is blocked (risk weight 25).
- Agent:
loan-review-agent(active, in-process reference agent, a deterministic rule set) - Synthetic applicants, one per control path:
| Case | Applicant | Agent recommends | Outcome |
|---|---|---|---|
case-loan-0001 | Strong file, $15,000 | Approve, limit $15,000 | Auto-finalized |
case-loan-0002 | Strong file, $40,000 | Approve, limit $40,000 | Human review (limit above threshold) |
case-loan-0003 | Score 598, DTI 51% | Decline with two adverse action reasons | Human review (decline) |
case-loan-0004 | No credit file | Decline with no reasons | Blocked (adverse action reasons missing) |
A lending agent's output uses the same shape as any other domain:
ts
const agentOutput = {
identity: { agent_id: "acme-underwriter", agent_version: "2.0.0", model_provider: "acme-ai", model_version: "uw-7", task_type: "loan_application_review" },
generated_at: new Date().toISOString(),
narrative: "Decline: debt-to-income ratio above policy.",
claims: [
{ claim_id: "r1", claim_type: "adverse_action_reason", statement: "Debt-to-income ratio of 51% exceeds the maximum of 43%.", supporting_source_ids: ["src-income"] },
],
recommendations: [
{ recommendation_id: "d1", category: "decline", item: "Decline $22,000 auto loan", instruction: "Send an adverse action notice.", rationale: "DTI above policy.", value: null, unit: null, supporting_source_ids: ["src-income"] },
],
required_sections: { adverse_action_reasons: true },
};Adding a domain
A domain pack is a package that exports a DomainPack (@carc/types): its definition (roles, task types, claim types, recommendation categories, risk weights), cases, seed agents, policies, in-process adapters, and an optional prompt_builder. Install it by adding it to the list the engine composes at boot (services/runtime-control-engine/src/index.ts). The engine validates every pack together at startup and refuses to start if a pack is inconsistent (a task type registered twice, a case or agent using another pack's task type, a policy scoped to another domain).