Skip to content

State Machine Architecture for Touring Deals

Executive Summary

This document defines a state machine architecture for modeling deals and handling what is currently referred to as Settling. The core innovation is treating deals as calculable state documents where settlements are derived from state rather than stored as separate entities.

Key Paradigm Shifts:

  • Settlements are calculated from state, not stored independently
  • Multiple "what-if" versions can exist simultaneously (Forecast, Over Performance, etc.)
  • Versions are Git-like: checkout → modify → submit
  • Templates define structure, instances define values
  • Cross-clause references enable tour-level aggregation without special constructs

Part 1: Core Concepts

1.1 Conceptual Hierarchy

DEAL (Top-level container)

  ├── deal_id, client_id, buyer_id, department_id, deal_type, metadata

  ├── Version Pointers
  │   ├── primary_version_id: "v_abc123"
  │   ├── forecast_primary_id: "v_def456"
  │   └── [custom_pointer_name]: "v_..."

  └── VERSIONS (Multiple independent snapshots)

      ├── Version 1 (Submitted - Immutable)
      │   ├── version_id, status: "submitted"
      │   ├── submitted_by, submitted_at
      │   └── clause_blocks: [...]

      ├── Version 2 (Submitted - Immutable)
      │   └── ...

      ├── Working Version A (Mutable - User editing)
      │   ├── version_id, status: "working"
      │   ├── created_from: "version_1_id"
      │   ├── locked_by: "user_123"
      │   └── clause_blocks: [...]

      └── Working Version B (Mutable - Different user)
          └── ...

CLAUSE_BLOCK (Execution unit within a version)

  ├── clause_block_id, name, reference (optional)
  ├── static_variables: { v_guarantee: 2500, v_artist_pct: 85 }

  ├── STATES (Calculated values with status)
  │   ├── SHOW_STATE: { value, status, calculation }
  │   └── SHOW_SETTLED: { value, status, calculation }

  ├── INPUTS (User-provided or sourced data)
  │   ├── TICKET_DETAIL_INPUT: { value, status }
  │   └── FACILITY_FEE_INPUT: { value, status }

  └── FINANCIAL_CLAUSES (Financial obligations)
      └── { name, amount, trigger, calculation, dates, payment_terms }

1.2 Key Definitions

ConceptDefinition
DealTop-level container holding all versions of a contract
VersionComplete snapshot of deal state; can be "working" (mutable) or "submitted" (immutable)
Clause BlockLogical grouping of states, inputs, and financial clauses for one aspect of a deal (e.g., one show, merch split)
StateCalculated value with status; can be overridden by user
InputUser-provided or externally-sourced data point
Financial ClauseFinancial obligation with trigger condition and calculation
TemplateReusable structure definition (without values) for creating deals

1.3 Status Codes

State Status Codes:

CodeNameMeaningCalculation Behavior
DDraftInitial entry, incompleteCalculation runs, sets value
FForecastProjected/estimated valuesCalculation runs, sets value
CConfirmedActual verified valuesCalculation runs, sets value
OOverrideUser manually set valueCalculation runs but does NOT set value

Input Status Codes:

CodeNameMeaning
PPendingInput expected but not yet received; user can still enter estimated values
CConfirmedInput received and verified
XNot RequiredInput not needed for this deal (clause won't trigger, OR condition, etc.)

Version Status:

StatusMeaningMutability
workingUser actively editingMutable (auto-saves)
submittedFinalized snapshotImmutable

Part 2: Version Lifecycle

2.1 Git-Like Workflow

┌─────────────────────────────────────────────────────────────┐
│                     DEAL: tour_spring_2025                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Submitted Versions (Immutable):                            │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐                 │
│  │  v_001  │    │  v_002  │    │  v_003  │                 │
│  │ Nov 15  │───▶│ Nov 20  │───▶│ Dec 1   │                 │
│  │ Initial │    │ Updated │    │ Post-   │                 │
│  │ Forecast│    │ Tickets │    │ Show    │                 │
│  └─────────┘    └─────────┘    └─────────┘                 │
│       │              │              │                       │
│       │              │              │                       │
│  Working Versions (Mutable):                                │
│       │              │              │                       │
│       ▼              │              ▼                       │
│  ┌─────────┐         │         ┌─────────┐                 │
│  │ w_abc   │         │         │ w_xyz   │                 │
│  │ User A  │         │         │ User B  │                 │
│  │ "What   │         │         │ "Final  │                 │
│  │  if?"   │         │         │ Settle" │                 │
│  └─────────┘         │         └─────────┘                 │
│                      │                                      │
│                      ▼                                      │
│                 ┌─────────┐                                 │
│                 │ w_def   │                                 │
│                 │ User C  │                                 │
│                 │ "Over   │                                 │
│                 │  Perf"  │                                 │
│                 └─────────┘                                 │
│                                                             │
│  Pointers:                                                  │
│  ├── primary: v_003                                         │
│  ├── forecast_optimistic: w_abc                             │
│  └── forecast_pessimistic: (none)                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.2 Version Operations

Create Working Version:

User action: "Create new working version from v_002"

1. System creates new version document
2. Deep copies all clause_blocks, states, inputs, financial_clauses from v_002
3. Sets status = "working"
4. Sets created_from = "v_002"
5. Sets locked_by = current_user
6. User can now modify

Modify Working Version:

User action: Changes TICKET_DETAIL_INPUT.quantity from 900 to 950

1. Input value updates immediately (auto-save)
2. Calculation engine runs on all dependent states
3. States with status != O get new calculated values
4. States with status == O keep user-provided values
5. Clause amounts recalculate based on triggers
6. UI reflects changes in real-time

Submit Working Version:

User action: "Submit this version"

1. System validates all references resolve
2. System validates no circular dependencies
3. System validates required inputs (non-X) have values
4. If valid:
   - Changes status from "working" to "submitted"
   - Sets submitted_by, submitted_at
   - Version becomes immutable
   - Removes locked_by
5. If invalid:
   - Returns validation errors
   - Version remains "working"

2.3 Version Independence

Critical Rule: Versions are 100% independent.

  • Modifying one version NEVER affects another
  • Each version is a complete, self-contained snapshot
  • No automatic propagation between versions
  • Cross-deal references must specify exact version_id
  • Pointers (primary, forecast, etc.) are just convenience labels

Part 3: Clause Block Structure

3.1 Anatomy of a Clause Block

javascript
{
  // Identification
  clause_block_id: "cb_fonda_guarantee_001",
  clause_block_ref: "CB_FONDA_GUARANTEE",  // Object reference for cross-block references
  name: "The Fonda - Guarantee vs NBOR",
  
  // Optional show reference (non-relational, can duplicate data)
  reference: {
    show_id: "show_fonda_dec15",
    venue: "The Fonda Theatre",
    city: "Los Angeles",
    date: "2025-12-15",
    capacity: 1200
  },
  
  // Static variables (set at initialization, can vary per clause_block)
  static_variables: {
    v_guarantee: 2500,
    v_artist_percentage: 85,
    v_tax_rate: 0.0825
  },
  
  // States (calculated values)
  states: [
    {
      state_key: "SHOW_STATE",
      // ... (see State structure)
    },
    {
      state_key: "SHOW_SETTLED",
      // ... (see State structure)
    }
  ],
  
  // Inputs (user-provided or sourced data)
  inputs: [
    {
      input_key: "TICKET_DETAIL_INPUT",
      // ... (see Input structure)
    }
  ],

  // Financial clauses (financial obligations)
  financial_clauses: [
    {
      clause_id: "clause_guarantee_vs_nbor",
      // ... (see Financial Clause structure)
    }
  ]
}

3.2 State Structure

javascript
{
  state_key: "SHOW_SETTLED",           // Unique key within clause_block
  state_type: "object",                // "boolean" | "number" | "string" | "object"
  name: "Show Settlement Data",        // Human-readable name
  
  // Current value (calculated or overridden)
  value: {
    status: "Ready",
    gross_box_office: 22500,
    sales_tax_collected: 1856.25,
    facility_fees: 2250,
    net_box_office_receipts: 18393.75
  },
  
  // Status determines if calculation overwrites value
  status: "F",                         // D, F, C, O
  
  // Calculation formula (expression language)
  calculation: {
    type: "aggregate",
    source: "SHOW_SETTLEMENT_INPUT",
    // OR more complex:
    type: "expression",
    expression: "TICKET_DETAIL_INPUT.total_amount - (FACILITY_FEE_INPUT.amount_per_ticket * TICKET_DETAIL_INPUT.quantity)"
  },
  
  // Metadata
  last_calculated_at: "2025-12-04T15:00:00Z",
  calculation_error: null              // If calculation failed, error message here
}

3.3 Input Structure

javascript
{
  input_key: "TICKET_DETAIL_INPUT",    // Unique key within clause_block
  input_type: "object",                // "boolean" | "number" | "string" | "object"
  name: "Ticket Sales Data",           // Human-readable name
  
  // Current value (user-provided or sourced)
  value: {
    tier: "general_admission",
    quantity: 900,
    price_per_ticket: 25.00,
    total_amount: 22500.00,
    sales_source: "box_office",
    notes: "Final box office count"
  },
  
  // Status
  status: "C",                         // P (Pending), C (Confirmed), X (Not Required)
  
  // Optional: source reference (if input comes from external system)
  source: {
    type: "show",                      // "show" | "external_api" | "user" | "other_clause_block"
    reference: "show_fonda_dec15",
    field_path: "ticket_sales"
  }
}

3.4 Clause Structure

javascript
{
  clause_id: "clause_guarantee_vs_nbor",
  name: "Guarantee vs NBOR for Show",
  
  // Calculated amount (result of calculation when trigger is true)
  amount: 15634.69,                    // 85% of 18393.75 = 15634.69 > 2500
  
  // Trigger condition (when does this clause apply?)
  trigger: {
    type: "expression",
    expression: "SHOW_SETTLED.value.status == 'Ready'"
  },
  
  // Calculation formula (how to compute amount when triggered)
  calculation: {
    type: "expression",
    expression: "MAX((SHOW_SETTLED.value.net_box_office_receipts * v_artist_percentage / 100), v_guarantee)"
  },
  
  // Date range (when is this clause effective)
  start_date: "2025-11-01",
  end_date: "2025-12-25",
  
  // Pre-condition (optional: must be true before trigger is even evaluated)
  pre_condition: null,
  
  // Metadata for calculation transparency
  calculation_metadata: {
    guarantee_component: 2500,
    percentage_component: 15634.69,
    winning_path: "percentage",        // "guarantee" or "percentage"
    calculation_summary: "MAX($2,500, 18,393.75 (NBOR) * 85% = 15,634.69)",
    last_calculated_at: "2025-12-04T15:00:00Z"
  }
}

Part 4: Calculation Engine Behavior

4.1 Calculation Flow

When a working version is modified:

User changes input value


┌──────────────────────────────────────┐
│ 1. Update input value (auto-save)    │
└──────────────────────────────────────┘


┌──────────────────────────────────────┐
│ 2. Build dependency graph            │
│    - Which states reference this     │
│      input?                          │
│    - Which states reference those    │
│      states?                         │
│    - Which clauses reference those   │
│      states?                         │
└──────────────────────────────────────┘


┌──────────────────────────────────────┐
│ 3. Topological sort (detect cycles)  │
│    - If cycle detected: report error │
│    - Continue with valid nodes       │
└──────────────────────────────────────┘


┌──────────────────────────────────────┐
│ 4. For each state in dependency      │
│    order:                            │
│                                      │
│    IF state.status == 'O':           │
│       → Skip (user override)         │
│       → Keep existing value          │
│                                      │
│    ELSE:                             │
│       → Run calculation              │
│       → If calculation succeeds:     │
│           state.value = result       │
│       → If calculation fails:        │
│           state.calculation_error =  │
│             error_message            │
│           Keep existing value        │
└──────────────────────────────────────┘


┌──────────────────────────────────────┐
│ 5. For each clause:                  │
│    - Evaluate trigger condition      │
│    - If trigger == true:             │
│        → Run calculation             │
│        → Set clause.amount           │
│    - If trigger == false:            │
│        → Set clause.amount = 0       │
└──────────────────────────────────────┘


┌──────────────────────────────────────┐
│ 6. Auto-status update:               │
│    - If input was missing (null)     │
│      and now has value               │
│    - AND state.status != 'O'         │
│    - THEN state.status = 'C'         │
└──────────────────────────────────────┘

4.2 Override Behavior

When status = O (Override):

State: SHOW_SETTLED
Status: O
Value: { net_box_office_receipts: 20000 }  // User manually entered
Calculation: TICKET_DETAIL_INPUT.total_amount - ...

What happens on recalc:
1. Calculation RUNS (for transparency)
2. Calculated result: 18393.75
3. BUT value remains 20000 (user override)
4. Optionally store: calculated_value: 18393.75 (for comparison)

Reverting Override:

User changes status from O → C (or D, F)

Next recalc:
1. Calculation runs
2. Value IS updated to calculated result
3. User override is discarded

4.3 Error Handling

Transient Errors (Non-Blocking):

javascript
// User types "$1000" instead of "1000"
{
  state_key: "SHOW_SETTLED",
  value: { ... },  // Keep previous value
  calculation_error: {
    type: "type_mismatch",
    message: "Cannot perform arithmetic on string '$1000'",
    field: "TICKET_DETAIL_INPUT.total_amount",
    timestamp: "2025-12-04T15:00:00Z"
  }
}

// UI shows error indicator on field
// User can fix and error clears
// Does NOT block saving working version

Reference Errors:

javascript
// Calculation references undefined input
{
  state_key: "SHOW_SETTLED",
  value: null,
  calculation_error: {
    type: "undefined_reference",
    message: "Reference 'UNDEFINED_INPUT' not found",
    timestamp: "2025-12-04T15:00:00Z"
  }
}

4.4 "Not Required" (X) Determination

Methods to mark input as X:

  1. User Manual: User sets status = X directly

  2. Pre-condition Evaluation:

    javascript
    // Clause has pre_condition that evaluates to false
    clause: {
      pre_condition: "SHOW_STATE.value == true",
      // If SHOW_STATE is false, all inputs for this clause → X
    }
  3. Post-calculation Detection:

    javascript
    // After calculation, detect input wasn't used
    // Example: MAX(A, B) where B > A, so A wasn't needed
    // System can suggest marking A's inputs as X
  4. OR Condition:

    javascript
    // Clause A OR Clause B triggers
    // If A triggers, B's inputs → X

Part 5: Cross-Clause References

5.1 Reference Syntax

Reference within same clause_block:

SHOW_SETTLED.value.net_box_office_receipts
v_artist_percentage
TICKET_DETAIL_INPUT.value.quantity

Reference to another clause_block (same version):

CB_FONDA_GUARANTEE.states.SHOW_SETTLED.value.net_box_office_receipts
CB_MERCH_SPLIT.financial_clauses.clause_merch_artist.amount

Reference to another deal/version (must specify version):

DEAL:deal_show_001:VERSION:v_abc123.clause_blocks.CB_GUARANTEE.financial_clauses.clause_main.amount

5.2 Tour Aggregation Example

javascript
// Tour Summary Clause Block
{
  clause_block_id: "cb_tour_summary",
  clause_block_ref: "CB_TOUR_SUMMARY",
  name: "Spring Tour 2025 - Total Settlement",
  
  static_variables: {},
  
  states: [
    {
      state_key: "TOTAL_ARTIST_PAYMENT",
      state_type: "number",
      name: "Total Artist Payment",
      value: 45234.56,
      status: "F",
      calculation: {
        type: "aggregation",
        expression: "SUM(cb.financial_clauses[0].amount for cb in [CB_SHOW_1, CB_SHOW_2, CB_SHOW_3] where cb.financial_clauses[0].trigger == true)"
      }
    }
  ],

  inputs: [],

  financial_clauses: [
    {
      clause_id: "clause_tour_total",
      name: "Tour Total Payment",
      amount: 45234.56,
      trigger: { expression: "true" },
      calculation: { expression: "TOTAL_ARTIST_PAYMENT.value" },
      start_date: "2025-03-01",
      end_date: "2025-03-31"
    }
  ]
}

5.3 Cross-Deal References

javascript
// Deal A references Deal B's submitted version
{
  input_key: "SHOW_1_PAYMENT",
  source: {
    type: "cross_deal",
    deal_id: "deal_show_001",
    version_id: "v_abc123",           // MUST specify version
    path: "clause_blocks.CB_GUARANTEE.financial_clauses.clause_main.amount"
  },
  value: 15000,                        // Resolved at working version creation
  status: "C"
}

Important: Cross-deal references are resolved once when working version is created. They do not auto-update if source deal changes. To get new values, create new working version.


Part 6: Deal-Level Structure

6.1 Complete Deal Document

javascript
{
  // Identification
  deal_id: "deal_spring_tour_2025",
  deal_type: "guarantee_vs_percentage_nbor",
  
  // Parties
  artist_id: "artist_taylor_swift",
  promoter_id: "promoter_live_nation",
  
  // Metadata
  name: "Spring Tour 2025 - West Coast Leg",
  created_at: "2025-01-15T10:00:00Z",
  created_by: "booking_agent_001",
  
  // Template reference (informational only, deal is independent)
  initialized_from_template: "tpl_guarantee_vs_nbor_v2",
  
  // Version pointers (convenience labels)
  version_pointers: {
    primary: "v_submitted_003",
    forecast_optimistic: "v_submitted_002",
    forecast_pessimistic: null,
    final_settlement: null
  },
  
  // All versions (both working and submitted)
  versions: [
    {
      version_id: "v_submitted_001",
      status: "submitted",
      submitted_at: "2025-01-20T14:00:00Z",
      submitted_by: "booking_agent_001",
      created_from: null,              // First version
      clause_blocks: [...]
    },
    {
      version_id: "v_submitted_002",
      status: "submitted",
      submitted_at: "2025-02-15T09:00:00Z",
      submitted_by: "booking_agent_001",
      created_from: "v_submitted_001",
      clause_blocks: [...]
    },
    {
      version_id: "v_submitted_003",
      status: "submitted",
      submitted_at: "2025-03-20T16:00:00Z",
      submitted_by: "finance_user_002",
      created_from: "v_submitted_002",
      clause_blocks: [...]
    },
    {
      version_id: "v_working_abc",
      status: "working",
      locked_by: "booking_agent_001",
      created_from: "v_submitted_003",
      last_auto_save: "2025-03-25T11:30:00Z",
      clause_blocks: [...]
    }
  ]
}

6.2 Version Document (Embedded in Deal)

javascript
{
  version_id: "v_submitted_003",
  status: "submitted",                 // "working" | "submitted"
  
  // Submission metadata (only for submitted versions)
  submitted_at: "2025-03-20T16:00:00Z",
  submitted_by: "finance_user_002",
  
  // Lineage
  created_from: "v_submitted_002",     // Which version this was branched from
  
  // Working version specific (only for working versions)
  // locked_by: "user_id",
  // last_auto_save: "timestamp",
  
  // The actual content
  clause_blocks: [
    {
      clause_block_id: "cb_show_1",
      clause_block_ref: "CB_SHOW_1",
      name: "Show 1 - The Fonda",
      // ... full clause block structure
    },
    {
      clause_block_id: "cb_show_2",
      clause_block_ref: "CB_SHOW_2",
      name: "Show 2 - The Greek",
      // ... full clause block structure
    },
    {
      clause_block_id: "cb_merch",
      clause_block_ref: "CB_MERCH",
      name: "Merchandise Split",
      // ... full clause block structure
    },
    {
      clause_block_id: "cb_tour_summary",
      clause_block_ref: "CB_TOUR_SUMMARY",
      name: "Tour Total",
      // ... aggregation clause block
    }
  ]
}

Part 7: Key Behaviors Summary

7.1 What DOES Auto-Update

ScenarioBehavior
User changes input in working versionAll dependent states recalculate
Input goes from null → value (status != O)State status → C
Calculation produces errorError stored, previous value kept
User changes state status from O → anything elseNext recalc updates value

7.2 What Does NOT Auto-Update

ScenarioBehavior
Source deal for cross-deal reference changesNo change; must create new working version
Template is modified after deal initializationNo change; deal is independent
Another version of same deal is modifiedNo change; versions are independent
User modifies different working versionNo change; completely isolated

7.3 Validation on Submit

Before a working version can be submitted:

  1. ✓ All references resolve (no undefined inputs/states/clause_blocks)
  2. ✓ No circular dependencies detected
  3. ✓ All required inputs (status != X) have values
  4. ✓ No unresolved calculation errors on required paths
  5. ✓ Cross-deal references point to valid submitted versions

Conclusion

This state machine architecture provides:

  • Flexibility: Any deal structure can be modeled with clause blocks
  • Transparency: All calculations visible and traceable
  • Control: Users can override any calculated value
  • Planning: What-if scenarios are first-class citizens
  • Auditability: Version history provides complete audit trail
  • Simplicity: No separate settlement documents to manage
  • Independence: Versions and deals are fully isolated

The architecture cleanly separates:

  • Structure (defined by templates)
  • Values (entered by users or calculated)
  • State (status codes indicating confidence level)
  • History (version snapshots)

This enables powerful modeling of complex touring deals while maintaining clarity and control.

Confidential. For internal use only.