Skip to content

Semantic Constants vs Hard-Coding: Developer Guide

Overview

This guide establishes the architectural distinction between legitimate model-specific behavior and forbidden hard-coded logic in ABS Platform service agents. Understanding this distinction is crucial for maintaining clean, data-driven architecture while preserving necessary service semantics.

The Fundamental Problem

Service agents must balance two competing concerns: 1. Data-Driven Architecture: All configuration must come from ServicePlanTemplate and setup data 2. Service Model Specificity: Agents must understand service-specific workflows and signal semantics

The challenge: Where is the line between necessary agent specificity and problematic hard-coding?

Core Definitions

❌ Hard-Coded Logic (FORBIDDEN)

Definition: Configuration data, identifiers, and business parameters that should vary by deployment, location, or business context.

Characteristics: - Varies by deployment environment - Business configuration parameters
- Location-specific identifiers - Operational constraints

Examples:

// ❌ FORBIDDEN: Hard-coded configuration
const HARD_CODED_VALUES = {
  station_ids: ['STATION_001', 'STATION_002'],
  fleet_id: 'fleet-batteries-nairobi',
  max_swaps_per_day: 10,
  grace_period_days: 7,
  allowed_locations: ['Nairobi', 'Mombasa'],
  service_hours: '09:00-18:00',
  pricing_tiers: ['standard', 'premium'],
  quota_limits: { daily: 5, monthly: 150 }
};

// ❌ FORBIDDEN: Hard-coded business logic
const badValidator = {
  check: () => location === 'Nairobi' && swaps_today < 10,
  signal: 'SERVICE_AVAILABLE'
};

✅ Model-Specific Behavior (LEGITIMATE)

Definition: Protocol-level constants and service semantics that define how a service model interprets signals and implements workflows.

Characteristics: - Part of the service model contract - Consistent across all deployments - Defines inter-process communication semantics - Workflow and signal interpretation rules

Examples:

// ✅ ALLOWED: Semantic constants for signal interpretation
const BSS_SIGNAL_SEMANTICS = {
  // IoT signal compression (protocol semantics)
  'battery_inserted': 'BATTERY_ISSUED',
  'battery_removed': 'BATTERY_RETURNED',
  'theft_detected': 'BATTERY_LOST',
  'customer_authenticated': 'ACCESS_GRANTED',

  // Service milestone definitions
  'swap_completed': 'SERVICE_MILESTONE_REACHED',
  'maintenance_required': 'ASSET_MAINTENANCE_DUE'
} as const;

// ✅ ALLOWED: FSM compression patterns (model behavior)
const FSM_COMPRESSION_RULES = {
  'SERVICE_ACTIVATED': 'service_cycle.DEPOSIT_CONFIRMED',
  'ASSET_ALLOCATED': 'service_cycle.BATTERY_ISSUED',
  'ASSET_RETURNED': 'service_cycle.BATTERY_RETURNED'
} as const;

// ✅ ALLOWED: Service-specific workflow semantics
const workflowValidator = {
  check: () => signal_type === 'battery_inserted' && customer_authenticated,
  signal: 'BATTERY_ISSUED'
};

Validation Framework

The Semantic Constant Test

Ask these questions to determine if a constant is legitimate:

interface SemanticConstantTest {
  // ✅ If ALL true → Semantic Constant (ALLOWED)
  definesSignalInterpretation: boolean;    // Does this define HOW the service interprets signals?
  partOfServiceContract: boolean;          // Is this part of the service model specification?
  consistentAcrossDeployments: boolean;    // Would this remain the same across all deployments?
  protocolSemantics: boolean;              // Does this define inter-process communication?
  workflowDefinition: boolean;             // Does this define service workflow patterns?
}

The Configuration Data Test

Ask these questions to identify hard-coded configuration:

interface ConfigurationDataTest {
  // ❌ If ANY true → Configuration Data (FORBIDDEN)
  variesByLocation: boolean;               // Does this change based on deployment location?
  variesByBusinessRules: boolean;          // Is this a business parameter that could change?
  deploymentSpecific: boolean;             // Is this specific to a deployment environment?
  operationalParameter: boolean;           // Is this an operational constraint/limit?
  identifierOrReference: boolean;          // Is this an ID, name, or reference?
}

Implementation Patterns

Pattern 1: Semantic Constants Module

Create a dedicated module for service semantics:

// File: bss-semantic-constants.ts
export const BSS_SEMANTICS = {
  // Signal interpretation constants
  IOT_SIGNALS: {
    'battery_inserted': 'BATTERY_ISSUED',
    'battery_removed': 'BATTERY_RETURNED',
    'theft_detected': 'BATTERY_LOST',
    'damage_reported': 'BATTERY_DAMAGED',
    'maintenance_completed': 'ASSET_RESTORED'
  },

  // MQTT topic semantics
  MQTT_PATTERNS: {
    'dt/arm/station/*/battery_inserted': 'IOT_BATTERY_INSERTION',
    'dt/arm/battery/*/theft_detected': 'IOT_THEFT_ALERT',
    'emit/odoo/subscription/*/payment_updated': 'BILLING_UPDATE'
  },

  // FSM compression mappings
  FSM_INPUTS: {
    'SERVICE_ACTIVATED': 'service_cycle.DEPOSIT_CONFIRMED',
    'ASSET_ALLOCATED': 'service_cycle.BATTERY_ISSUED',
    'ASSET_RETURNED': 'service_cycle.BATTERY_RETURNED',
    'PAYMENT_CONFIRMED': 'payment_cycle.RENEWAL_PAID'
  },

  // Service milestone definitions
  MILESTONES: {
    'swap_initiated': 'SWAP_START',
    'swap_completed': 'SWAP_COMPLETE',
    'service_terminated': 'SERVICE_END'
  }
} as const;

Pattern 2: Configuration Extraction

Replace hard-coded values with data-driven extraction:

// ❌ BEFORE: Hard-coded business logic
const oldValidator = {
  check: () => station_id === 'STATION_001' && 
               location === 'Nairobi' && 
               swaps_today < 10,
  signal: 'SERVICE_AVAILABLE'
};

// ✅ AFTER: Data-driven configuration
const newValidator = {
  check: () => getAllowedStations(template).includes(station_id) &&
               getTemplateLocation(template) === location &&
               getCurrentSwaps(planState) < getMaxSwaps(template),
  signal: 'SERVICE_AVAILABLE'
};

Pattern 3: Validator Arrays with Semantics

Combine semantic constants with the Validator Array Pattern:

// ✅ RECOMMENDED: Clean semantic-driven validation
const iotSignalValidators = [
  {
    check: () => signal_type === BSS_SEMANTICS.IOT_SIGNALS.battery_inserted && 
                 customer_authenticated,
    signal: 'BATTERY_ISSUED'
  },
  {
    check: () => signal_type === BSS_SEMANTICS.IOT_SIGNALS.theft_detected,
    signal: 'BATTERY_LOST'
  },
  {
    check: () => signal_type === BSS_SEMANTICS.IOT_SIGNALS.maintenance_completed &&
                 personnel_verified,
    signal: 'ASSET_RESTORED'
  }
];

Pattern 4: Configuration Helper Functions

Create helper functions to extract data from templates:

// Configuration extraction utilities
export const TemplateUtils = {
  getAllowedStations: (template: ServicePlanTemplate): string[] => {
    return template.service_bundle.services
      .filter(s => s.asset_type === 'FLEET')
      .map(s => extractStationIds(s.asset_reference))
      .flat();
  },

  getMaxSwapsPerDay: (template: ServicePlanTemplate): number => {
    const quotaConfig = template.agent_config.quota_settings;
    return quotaConfig?.daily_limit ?? Infinity;
  },

  getGracePeriodDays: (template: ServicePlanTemplate): number => {
    return template.contract_terms.grace_period_days;
  },

  getAllowedBatteryTypes: (template: ServicePlanTemplate): string[] => {
    return template.service_bundle.services
      .filter(s => s.asset_type === 'FLEET')
      .map(s => s.access_control.battery_types)
      .flat();
  }
};

Architecture Benefits

1. Clear Separation of Concerns

  • Semantic constants: Define service model behavior (agent responsibility)
  • Configuration data: Varies by deployment (template responsibility)
  • Clean boundaries: Prevents mixing protocol semantics with business config

2. Maintainability

  • Protocol changes: Update semantic constants (rare, controlled changes)
  • Business changes: Update setup data (frequent, data-driven changes)
  • Version management: Semantic constants versioned with agent, config with templates

3. Testability

  • Unit tests: Validate signal compression logic using semantic constants
  • Integration tests: Test various configurations using different templates
  • Consistent behavior: Same semantic constants across test environments

4. Scalability

  • New deployments: Only require new template configurations
  • Service expansion: Semantic constants define consistent service behavior
  • Multi-region: Same agent logic, different configuration data

Common Mistakes and Solutions

Mistake 1: Location-Specific Logic

// ❌ WRONG: Hard-coded location logic
if (location === 'Nairobi') {
  return { maxSwaps: 10, currency: 'KES' };
}

// ✅ CORRECT: Template-driven location logic
return {
  maxSwaps: getMaxSwaps(template),
  currency: getTemplateCurrency(template)
};

Mistake 2: Business Rule Constants

// ❌ WRONG: Hard-coded business rules
const GRACE_PERIOD_DAYS = 7;
const MAX_OUTSTANDING_AMOUNT = 5000;

// ✅ CORRECT: Configuration-driven business rules
const gracePeriod = template.contract_terms.grace_period_days;
const maxOutstanding = template.contract_terms.credit_limit;

Mistake 3: Service Identifier Dependencies

// ❌ WRONG: Hard-coded service dependencies
if (serviceId === 'svc-battery-fleet-kenya-premium') {
  return { tier: 'premium', batteryTypes: ['TYPE_A', 'TYPE_B'] };
}

// ✅ CORRECT: Service metadata extraction
const service = getServiceById(template, serviceId);
return {
  tier: service.access_control.quality_tier,
  batteryTypes: service.access_control.battery_types
};

Testing Guidelines

Test Semantic Constants

describe('BSS Signal Semantics', () => {
  it('should compress IoT signals correctly', () => {
    const signal = BSS_SEMANTICS.IOT_SIGNALS.battery_inserted;
    expect(signal).toBe('BATTERY_ISSUED');
  });

  it('should map FSM inputs consistently', () => {
    const fsmInput = BSS_SEMANTICS.FSM_INPUTS.ASSET_ALLOCATED;
    expect(fsmInput).toBe('service_cycle.BATTERY_ISSUED');
  });
});

Test Configuration Extraction

describe('Template Configuration', () => {
  it('should extract station IDs from template', () => {
    const stations = TemplateUtils.getAllowedStations(testTemplate);
    expect(stations).toEqual(['STA-001', 'STA-002']);
  });

  it('should handle different business rules per template', () => {
    const maxSwaps = TemplateUtils.getMaxSwapsPerDay(premiumTemplate);
    expect(maxSwaps).toBe(20); // Premium template allows more swaps
  });
});

Migration Strategy

Step 1: Identify Hard-Coded Values

# Search for potential hard-coded values
grep -r "STATION_" src/
grep -r "fleet-" src/
grep -r "max.*:" src/

Step 2: Extract Semantic Constants

// Create semantic constants module
export const SERVICE_SEMANTICS = {
  // Move signal interpretation logic here
};

Step 3: Create Configuration Helpers

// Create template utility functions
export const ConfigUtils = {
  // Move configuration extraction logic here
};

Step 4: Update Validators

// Replace hard-coded values in validator arrays
const updatedValidators = validators.map(v => ({
  check: () => replaceHardCodedLogic(v.check),
  signal: v.signal
}));

Conclusion

The distinction between semantic constants and hard-coded logic is fundamental to maintaining clean, scalable service architecture:

  • Semantic Constants: Enable service model specificity while maintaining consistency
  • Configuration Data: Ensures flexibility and data-driven behavior
  • Clear Boundaries: Prevent mixing protocol semantics with business configuration

Following these patterns ensures that ABS Platform service agents remain both service-specific and deployment-agnostic, supporting the core architectural principles of the DIRAC Framework.