Skip to main content

Decision Contract

The Decision object is the stable, typed contract returned by all TealTiger components in v1.1.x.

Overview

Every policy evaluation, guardrail check, and circuit breaker decision returns a Decision object with:
  • Deterministic action (ALLOW, DENY, REQUIRE_APPROVAL, etc.)
  • Machine-readable reason codes
  • Risk score (0-100)
  • Correlation ID for traceability
  • Component versions for audit defensibility

Interface

interface Decision {
  // Core decision
  action: DecisionAction;
  reason_codes: ReasonCode[];
  risk_score: number; // 0-100
  
  // Policy context
  mode: PolicyMode;
  policy_id?: string;
  policy_version?: string;
  
  // Traceability
  correlation_id: string;
  trace_id?: string;
  
  // Metadata
  provider?: string;
  reason?: string;
  component_versions: ComponentVersions;
  metadata?: Record<string, any>;
}

Decision Actions

enum DecisionAction {
  ALLOW = 'ALLOW',
  DENY = 'DENY',
  REQUIRE_APPROVAL = 'REQUIRE_APPROVAL',
  REDACT = 'REDACT',
  TRANSFORM = 'TRANSFORM',
  DEGRADE = 'DEGRADE'
}

Action Semantics

ActionMeaningUse Case
ALLOWRequest is compliant, proceedNormal operation
DENYRequest violates policy, blockSecurity violation, cost limit exceeded
REQUIRE_APPROVALRequest needs human reviewHigh-risk operation, sensitive data access
REDACTRequest contains PII, redact before proceedingPII detection, data privacy
TRANSFORMRequest needs modificationContent moderation, prompt injection mitigation
DEGRADEUse fallback/cheaper modelCost optimization, rate limiting

Reason Codes

enum ReasonCode {
  // Security
  PROMPT_INJECTION = 'PROMPT_INJECTION',
  PII_DETECTED = 'PII_DETECTED',
  UNSAFE_CONTENT = 'UNSAFE_CONTENT',
  
  // Cost
  COST_LIMIT_EXCEEDED = 'COST_LIMIT_EXCEEDED',
  TOKEN_LIMIT_EXCEEDED = 'TOKEN_LIMIT_EXCEEDED',
  
  // Reliability
  CIRCUIT_OPEN = 'CIRCUIT_OPEN',
  RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',
  
  // Governance
  POLICY_VIOLATION = 'POLICY_VIOLATION',
  APPROVAL_REQUIRED = 'APPROVAL_REQUIRED'
}

Risk Score

The risk_score is a normalized value from 0-100:
  • 0-30: Low risk (informational)
  • 31-60: Medium risk (monitor)
  • 61-85: High risk (review)
  • 86-100: Critical risk (block)
Risk scores are calculated based on:
  • Policy violation severity
  • Number of violations
  • Domain-specific risk factors (security, cost, reliability)

Component Versions

interface ComponentVersions {
  sdk: string;
  engine: string;
  guard?: string;
  circuit?: string;
  monitor?: string;
}
Component versions enable audit defensibility - you can reproduce decisions by knowing exactly which versions were used.

Usage Examples

Basic Decision Handling

import { TealEngine, DecisionAction } from '@tealtiger/sdk';

const engine = new TealEngine(config);
const decision = await engine.evaluate(request, context);

switch (decision.action) {
  case DecisionAction.ALLOW:
    return await makeRequest(request);
    
  case DecisionAction.DENY:
    throw new PolicyViolationError(
      `Request denied: ${decision.reason}`,
      decision.reason_codes
    );
    
  case DecisionAction.REQUIRE_APPROVAL:
    return await requestApproval(request, decision);
    
  case DecisionAction.REDACT:
    const redacted = await redactPII(request);
    return await makeRequest(redacted);
    
  default:
    throw new Error(`Unexpected action: ${decision.action}`);
}

Risk-Based Routing

const decision = await engine.evaluate(request, context);

if (decision.risk_score >= 86) {
  // Critical risk - block and alert
  await alertSecurityTeam(decision);
  throw new SecurityViolationError(decision);
  
} else if (decision.risk_score >= 61) {
  // High risk - require approval
  return await requestApproval(request, decision);
  
} else if (decision.risk_score >= 31) {
  // Medium risk - log and proceed
  await audit.log('medium_risk_request', decision);
  return await makeRequest(request);
  
} else {
  // Low risk - proceed normally
  return await makeRequest(request);
}

Reason Code Handling

const decision = await engine.evaluate(request, context);

if (decision.reason_codes.includes(ReasonCode.PII_DETECTED)) {
  // Handle PII detection
  const redacted = await redactPII(request);
  return await makeRequest(redacted);
}

if (decision.reason_codes.includes(ReasonCode.COST_LIMIT_EXCEEDED)) {
  // Handle cost limit
  const degraded = await useCheaperModel(request);
  return await makeRequest(degraded);
}

if (decision.reason_codes.includes(ReasonCode.CIRCUIT_OPEN)) {
  // Handle circuit breaker
  return await useFallback(request);
}

Mode-Specific Behavior

The Decision.mode field indicates which policy mode was active:
const decision = await engine.evaluate(request, context);

if (decision.mode === PolicyMode.REPORT_ONLY) {
  // Decision is informational only, always proceed
  await audit.log('report_only_decision', decision);
  return await makeRequest(request);
}

if (decision.mode === PolicyMode.MONITOR) {
  // Decision is logged but not enforced
  if (decision.action === DecisionAction.DENY) {
    await audit.log('would_have_denied', decision);
  }
  return await makeRequest(request);
}

if (decision.mode === PolicyMode.ENFORCE) {
  // Decision is enforced
  if (decision.action === DecisionAction.DENY) {
    throw new PolicyViolationError(decision);
  }
  return await makeRequest(request);
}

Backwards Compatibility

The Decision interface is backwards compatible with v1.0.x PolicyEvaluationResult:
// v1.0.x code continues to work
const result = await engine.evaluate(request);
if (result.action === 'DENY') {
  throw new Error('Denied');
}

// v1.1.x adds new fields but doesn't break existing code
const decision = await engine.evaluate(request, context);
console.log(decision.correlation_id); // New in v1.1.x
console.log(decision.risk_score); // New in v1.1.x