Deal Instance Specification v1.3
Document Purpose: Define the complete runtime structure, computation model, and state management for Deal Instances in the Deal Engine architecture revision.
Status: Draft for review
Version: 1.3
1. Overview
1.1 What is a Deal Instance?
A Deal Instance is a self-contained, executable representation of a specific contract. It contains:
- All deal-level data (parties, dates, territory)
- All clause instances (each with its own data and copied logic)
- Deal-level logic for aggregation
- All computed state (event states, calculated values)
Deal instances have no runtime dependencies on the clause type or deal type catalogs. Once created, they are complete snapshots that can be evaluated independently.
Deal instances are versioned. Every change—whether to data or logic—creates a new immutable version. The version chain provides a complete audit trail of the deal's evolution from creation through all amendments.
Deal Instance (version chain)
├── Version 1 (immutable) ─── Initial deal
├── Version 2 (immutable) ─── Show 1 settled
├── Version 3 (immutable) ─── Show 2 settled
├── Version 4 (immutable) ─── Bonus structure amended
└── Version 5 (current) ─── Show 3 settled1.2 Self-Containment Principle
When a deal is created:
- Deal-level schema is populated with actual data
- Selected clause types have their logic copied into the instance
- Clause data is populated
- Deal-level logic is copied into the instance
The result is like a compiled program—it contains everything needed to execute without referencing source libraries.
Deal Instance (Current Version)
├── version_info // Version number, effective date, amendment details
├── deal_data // Actual values (parties, dates, etc.)
├── clauses[] // Each active clause instance:
│ ├── data // - Instance-specific data
│ ├── logic // - Copied from clause type (frozen)
│ └── status // - active, superseded, or removed
├── archived_clauses[] // Superseded/removed clauses (frozen final state)
├── deal_logic // Copied from deal type (frozen)
└── computed_state // All calculated values and event states
│
└── prior_version → [Previous Version] → [Previous Version] → ...Each version is immutable. When changes occur, a new version is created containing the updated state. Prior versions are preserved for audit and historical queries.
1.3 Why Self-Containment?
| Benefit | Explanation |
|---|---|
| Immutability | Contract terms don't change if clause types are updated |
| Auditability | Exact logic that produced results is preserved |
| Independence | No external dependencies at runtime |
| Legal validity | Frozen logic matches contractual agreement |
| Reproducibility | Same inputs always produce same outputs |
| Version history | Every version is a complete, queryable snapshot |
2. Deal Instance Structure
2.1 Top-Level Structure
{
"instance_metadata": { ... },
"version_info": { ... },
"deal_data": { ... },
"clauses": [ ... ],
"archived_clauses": [ ... ],
"deal_logic": { ... },
"computed_state": { ... }
}| Section | Purpose |
|---|---|
instance_metadata | System information about the deal instance itself |
version_info | Version number, effective date, amendment details |
deal_data | Deal-level data (parties, dates, territory, etc.) |
clauses | Array of active clause instances, each with data and logic |
archived_clauses | Superseded or removed clauses with frozen final state |
deal_logic | Deal-level events and computations (copied from deal type) |
computed_state | All calculated values, event states, outputs |
2.2 Instance Metadata
{
"instance_metadata": {
"instance_id": "deal-2024-001234",
"deal_type_ref": {
"id": "music-touring",
"version": "1.0.0"
},
"created_at": "2024-03-15T10:30:00Z",
"created_by": "user@agency.com",
"current_version": 5,
"status": "active"
}
}| Field | Description |
|---|---|
instance_id | Unique identifier for this deal instance |
deal_type_ref | Reference to originating deal type (informational only) |
created_at | When instance was first created (version 1) |
created_by | Who created the instance |
current_version | Latest version number |
status | Current deal status |
Note: Version-specific information (effective date, amendment details) is in version_info, not metadata.
Note: deal_type_ref is informational only—the instance does not depend on the deal type at runtime.
2.3 Version Info
{
"version_info": {
"version": 5,
"effective_date": "2024-08-01",
"created_at": "2024-08-01T14:30:00Z",
"created_by": "user@agency.com",
"prior_version": 4,
"change_type": "data_update",
"change_summary": "Show 3 settlement data entered",
"amendment": null
}
}| Field | Required | Description |
|---|---|---|
version | Yes | Sequential version number (starts at 1) |
effective_date | Yes | Date this version takes effect |
created_at | Yes | Timestamp when version was created |
created_by | Yes | Who created this version |
prior_version | No | Previous version number (null for version 1) |
change_type | Yes | Type of change that triggered this version |
change_summary | Yes | Human-readable description of change |
amendment | No | Amendment details (for logic/clause changes) |
Change types:
| Change Type | Description | Triggers Recalculation |
|---|---|---|
initial | First version (deal creation) | Full calculation |
data_update | Clause or deal data changed | Full recalculation |
logic_amendment | Clause logic modified | Full recalculation from inception |
clause_addition | New clause added | Full recalculation |
clause_replacement | Clause deactivated and replaced | Full recalculation (excluding archived) |
clause_removal | Clause deactivated | Full recalculation (excluding archived) |
deal_logic_amendment | Deal-level logic modified | Full recalculation |
Amendment Details
When a version involves a logic change (not just data), the amendment field captures details:
{
"version_info": {
"version": 4,
"effective_date": "2024-07-15",
"created_at": "2024-07-15T10:30:00Z",
"created_by": "agent@agency.com",
"prior_version": 3,
"change_type": "clause_replacement",
"change_summary": "Renegotiated bonus structure per Amendment 2",
"amendment": {
"amendment_id": "AMD-002",
"reason": "Artist requested simplified bonus tiers",
"document_ref": "contracts/deal-2024-001234/amendment-002.pdf",
"authorized_by": "agent-jane-doe",
"effective_date": "2024-07-15",
"changes": [
{
"action": "deactivate",
"clause_id": "bonus_structure_v1",
"reason": "Superseded by new structure"
},
{
"action": "add",
"clause_id": "bonus_structure_v2",
"clause_type_ref": {
"id": "tiered-bonus",
"version": "1.0.0"
},
"replaces": "bonus_structure_v1"
}
]
}
}
}| Field | Description |
|---|---|
amendment_id | External amendment identifier |
reason | Business reason for the amendment |
document_ref | Reference to legal amendment document |
authorized_by | Who authorized the amendment |
effective_date | When the amendment takes effect |
changes | Array of specific changes made |
Change actions:
| Action | Description |
|---|---|
add | New clause added |
deactivate | Existing clause deactivated (superseded or removed) |
modify_logic | Clause logic changed in place |
modify_deal_logic | Deal-level logic changed |
2.4 Deal Data
Deal-level data populated from the deal type schema:
{
"deal_data": {
"parties": {
"talent": {
"id": "talent-12345",
"name": "Big Talent",
"legal_entity": "Big Talent Touring LLC"
},
"promoter": {
"id": "promoter-67890",
"name": "Big Promoter Entertainment",
"legal_entity": "Big Promoter Worldwide Inc."
},
"agency": {
"id": "agency-001",
"name": "United Talent Agency"
}
},
"dates": {
"effective_date": "2024-03-01",
"tour_start": "2024-06-15",
"tour_end": "2024-09-30"
},
"currency": "USD",
"territory": {
"primary": "North America",
"secondary": ["Canada"],
"exclusions": []
},
"tour_info": {
"tour_name": "Summer 2024 Tour",
"tour_region": "North America",
"headliner": true
}
}
}2.5 Clause Instances
Each clause instance contains status fields for versioning:
{
"clauses": [
{
"clause_id": "show_settlement",
"clause_type_ref": {
"id": "show-settlement",
"version": "1.0.0"
},
"category": "guarantee",
"value_type": "earning",
"status": "active",
"effective_from": "2024-03-15",
"effective_until": null,
"replaces": null,
"superseded_by": null,
"data": { ... },
"logic": { ... }
},
{
"clause_id": "bonus_structure_v2",
"clause_type_ref": {
"id": "tiered-bonus",
"version": "1.0.0"
},
"category": "contingent",
"value_type": "earning",
"status": "active",
"effective_from": "2024-07-15",
"effective_until": null,
"replaces": "bonus_structure_v1",
"superseded_by": null,
"data": { ... },
"logic": { ... }
}
]
}| Field | Description |
|---|---|
clause_id | Instance identifier (unique within deal) |
clause_type_ref | Reference to originating clause type (informational) |
category | guarantee, contingent, or simple |
value_type | earning, reimbursement, third_party, or in_kind |
status | active, superseded, or removed |
effective_from | Date this clause became active |
effective_until | Date this clause was deactivated (null if active) |
replaces | clause_id this clause replaces (null if original) |
superseded_by | clause_id that superseded this clause (null if active) |
data | Clause-specific data |
logic | Frozen DSL logic copied from clause type |
Clause status values:
| Status | Description |
|---|---|
active | Clause is currently in effect |
superseded | Clause was replaced by another clause |
removed | Clause was removed without replacement |
2.6 Archived Clauses
When a clause is superseded or removed, it moves to archived_clauses with its final computed state frozen:
{
"archived_clauses": [
{
"clause_id": "bonus_structure_v1",
"clause_type_ref": {
"id": "tiered-bonus",
"version": "1.0.0"
},
"category": "contingent",
"value_type": "earning",
"status": "superseded",
"effective_from": "2024-03-15",
"effective_until": "2024-07-15",
"superseded_by": "bonus_structure_v2",
"archived_at_version": 4,
"data": { ... },
"logic": { ... },
"final_computed_state": {
"events": {
"any_bonus_earned": { "state": "true", "occurred_at": "2024-06-01" }
},
"outputs": {
"total_earned": 25000,
"total_received": 25000
}
}
}
]
}| Field | Description |
|---|---|
archived_at_version | Version number when clause was archived |
final_computed_state | Frozen computed state at time of archival |
Critical rule: Archived clauses are never recalculated. Their final_computed_state represents the actual financial activity that occurred under that clause's terms.
2.7 Clause Data Structure
{
"data": {
"shows": [
{
"id": "show_01",
"date": "2024-06-15",
"venue": "Madison Square Garden",
"city": "New York",
"guarantee": 150000,
"gross_revenue": null,
"expenses": null,
"occurred": false,
"settled": false,
"artist_share": null,
"earned": null
},
{
"id": "show_02",
"date": "2024-06-18",
"venue": "TD Garden",
"city": "Boston",
"guarantee": 125000,
"gross_revenue": 450000,
"expenses": 85000,
"occurred": true,
"settled": true,
"artist_share": null,
"earned": null
}
],
"artist_percentage": 0.85,
"earning_schedule": {
"pattern": "event_triggered",
"trigger": "show_settled"
},
"receipt_schedule": {
"pattern": "event_triggered",
"trigger": "show_settled",
"days_after": 30
}
}
}Data categories:
| Category | Description | Example Fields |
|---|---|---|
| Input data | Values from contract | guarantee, artist_percentage |
| External data | Updated during deal lifecycle | gross_revenue, expenses |
| State flags | Boolean states | occurred, settled |
| Computed fields | Populated by logic (start as null) | artist_share, earned |
| Schedules | Embedded schedule definitions | earning_schedule, receipt_schedule |
2.8 Clause Logic Structure
Logic is stored as structured DSL (not raw text):
{
"logic": {
"events": [
{
"name": "show_occurred",
"description": "Show has occurred",
"condition": {
"type": "field_equals",
"field": "item.occurred",
"value": true
},
"scope": "for_each",
"collection": "shows"
},
{
"name": "all_settled",
"description": "All shows have been settled",
"condition": {
"type": "comparison",
"left": {
"type": "count",
"collection": "shows",
"where": { "field": "settled", "equals": true }
},
"operator": "==",
"right": {
"type": "count",
"collection": "shows"
}
}
}
],
"for_each": [
{
"collection": "shows",
"item_alias": "show",
"computations": [
{
"name": "net_revenue",
"expression": {
"type": "subtract",
"left": { "type": "field", "path": "show.gross_revenue" },
"right": { "type": "field", "path": "show.expenses" }
}
},
{
"name": "artist_share",
"expression": {
"type": "multiply",
"left": { "type": "variable", "name": "net_revenue" },
"right": { "type": "field", "path": "artist_percentage" }
},
"target": "show.artist_share"
},
{
"name": "earned",
"expression": {
"type": "max",
"args": [
{ "type": "field", "path": "show.guarantee" },
{ "type": "variable", "name": "artist_share" }
]
},
"target": "show.earned"
}
]
}
],
"computations": [
{
"name": "total_guarantee",
"expression": {
"type": "sum",
"collection": "shows",
"field": "guarantee"
}
},
{
"name": "total_earned",
"expression": {
"type": "sum_coalesce",
"collection": "shows",
"field": "earned",
"default": 0
}
}
],
"outputs": [
"total_guarantee",
"total_earned",
"total_received",
"all_settled"
],
"financial": {
"amount": { "type": "output", "name": "total_earned" },
"earned": { "type": "schedule", "ref": "earning_schedule" },
"received": { "type": "schedule", "ref": "receipt_schedule" }
}
}
}2.9 Deal-Level Logic
Copied from deal type, handles cross-clause aggregation:
{
"deal_logic": {
"events": [
{
"name": "all_shows_settled",
"description": "All shows in the tour have been settled",
"condition": {
"type": "clause_output",
"clause": "show_settlement",
"output": "all_settled",
"coalesce": false
}
},
{
"name": "tour_complete",
"description": "Tour fully settled including versus",
"condition": {
"type": "and",
"args": [
{ "type": "event", "name": "all_shows_settled" },
{
"type": "clause_output",
"clause": "tour_versus",
"output": "settled",
"coalesce": true
}
]
}
}
],
"computations": [
{
"name": "total_guaranteed",
"expression": {
"type": "add",
"args": [
{
"type": "clause_output",
"clause": "show_settlement",
"output": "total_guarantee",
"coalesce": 0
},
{
"type": "clause_output",
"clause": "tour_versus",
"output": "tour_guarantee",
"coalesce": 0
}
]
}
},
{
"name": "total_earned",
"expression": {
"type": "add",
"args": [
{
"type": "clause_output",
"clause": "show_settlement",
"output": "total_earned",
"coalesce": 0
},
{
"type": "clause_output",
"clause": "tour_versus",
"output": "versus_earned",
"coalesce": 0
}
]
}
}
],
"outputs": [
"total_guaranteed",
"total_earned",
"total_received",
"total_pending",
"tour_complete"
]
}
}2.10 Computed State
All computed values stored after evaluation:
{
"computed_state": {
"computed_at": "2024-06-20T14:22:17Z",
"computation_version": 47,
"clause_states": {
"show_settlement": {
"events": {
"all_occurred": false,
"all_settled": false
},
"outputs": {
"total_guarantee": 1250000,
"total_earned": 485000,
"total_received": 320000,
"all_settled": false
},
"item_states": {
"show_01": {
"events": { "occurred": false, "settled": false },
"computed": { "artist_share": null, "earned": null }
},
"show_02": {
"events": { "occurred": true, "settled": true },
"computed": { "artist_share": 310250, "earned": 310250 }
}
}
},
"tour_versus": {
"events": {
"settled": false
},
"outputs": {
"tour_guarantee": 500000,
"versus_earned": null,
"settled": false
}
}
},
"deal_outputs": {
"total_guaranteed": 1750000,
"total_earned": 485000,
"total_received": 320000,
"total_pending": 165000,
"tour_complete": false
},
"deal_events": {
"all_shows_settled": false,
"tour_complete": false
}
}
}3. Computation Model
3.1 Reactive Full-Recalculation
The Deal Engine uses reactive full-recalculation: any change triggers complete re-evaluation of the entire deal state.
Input Change (data update, event trigger)
↓
Full Recomputation
├── Evaluate all clause instances
│ ├── Process for_each loops
│ ├── Evaluate clause events
│ └── Compute clause outputs
├── Evaluate deal-level logic
│ ├── Aggregate clause outputs
│ └── Evaluate deal events
└── Store computed state
↓
New Computed State (immutable snapshot)3.2 Evaluation Order
Computation follows a strict order to ensure deterministic results:
1. CLAUSE EVALUATION (for each clause instance)
1.1 Evaluate for_each loops (process collections)
- For each item in collection:
- Evaluate item-scoped events
- Compute item-scoped values
- Populate computed fields in data
1.2 Evaluate clause-level events
1.3 Compute clause-level values
1.4 Populate clause outputs
2. DEAL-LEVEL EVALUATION
2.1 Gather clause outputs (via @clause.output references)
2.2 Evaluate deal-level events
2.3 Compute deal-level aggregations
2.4 Populate deal outputs
3. STATE STORAGE
3.1 Increment computation_version
3.2 Store all computed values
3.3 Store all event states
3.4 Record timestamp3.3 Dependency Resolution
Within each evaluation phase, computations are ordered by dependency:
Example: Clause computations
net_revenue = gross_revenue - expenses // No dependencies
artist_share = net_revenue * artist_percentage // Depends on net_revenue
earned = max(guarantee, artist_share) // Depends on artist_share
Evaluation order: net_revenue → artist_share → earnedCircular dependencies are invalid and detected at deal creation time.
3.4 Null Handling
The ?? (null coalescing) operator provides defaults for null values:
sum(@bonus_groups[*].earned ?? 0)Null propagation rules:
| Expression | If any operand is null | Result |
|---|---|---|
a + b | Propagates | null |
a * b | Propagates | null |
a > b | Propagates | null (not false) |
a ?? default | Uses default | default |
sum(collection) | Skips nulls | Sum of non-null values |
count(collection) | Counts all | Total count (including null) |
3.5 Event State Model
Events use a three-state model:
| State | Meaning |
|---|---|
unknown | Not yet determinable |
true | Condition satisfied |
false | Condition definitively not satisfied |
State transitions:
Initial: unknown
↓
Condition evaluates to true → true
Condition evaluates to false → false (or remains unknown if data missing)Event state in computed_state:
{
"events": {
"all_settled": {
"state": "false",
"evaluated_at": "2024-06-20T14:22:17Z",
"transition_history": [
{
"from": "unknown",
"to": "false",
"at": "2024-06-20T14:22:17Z",
"reason": "3 of 42 shows settled"
}
]
}
}
}4: Instance Versioning
4.1 Version Chain Model
Every deal instance maintains a chain of versions. Each version is an immutable snapshot capturing the complete deal state at a point in time.
┌─────────────────────────────────────────────────────────────────────┐
│ Deal Instance │
│ instance_id: deal-2024-001234 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Ver 1 │───▶│ Ver 2 │───▶│ Ver 3 │───▶│ Ver 4 │ │
│ │ Initial │ │ Data │ │ Data │ │ Clause │ │
│ │ │ │ Update │ │ Update │ │ Replace │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ [computed [computed [computed [computed │
│ state 1] state 2] state 3] state 4] │
│ │
└─────────────────────────────────────────────────────────────────────┘4.2 Version Creation Triggers
| Trigger | Change Type | What Happens |
|---|---|---|
| Show data entered | data_update | New version, full recalculation |
| Settlement recorded | data_update | New version, full recalculation |
| Event state changed | data_update | New version, full recalculation |
| Clause logic amended | logic_amendment | New version, full recalculation from inception |
| New clause added | clause_addition | New version, full recalculation |
| Clause replaced | clause_replacement | New version, old clause archived, full recalculation |
| Clause removed | clause_removal | New version, clause archived, full recalculation |
| Deal logic amended | deal_logic_amendment | New version, full recalculation |
4.3 Effective Dates
Every version has an effective_date indicating when that version's state takes effect.
Rules:
effective_datemust be ≥ prior version'seffective_date- For data updates,
effective_dateis typically the date of the change - For amendments,
effective_datemay be backdated to when terms actually changed - Queries for "state at date X" find the version with the latest
effective_date≤ X
Timeline:
─────────────────────────────────────────────────────────────▶
│ │ │ │
Mar 15 Jun 1 Jul 15 Aug 1
Ver 1 Ver 2 Ver 3 Ver 4
(initial) (show 1) (amendment) (show 2)
(eff: Jul 1)
Query: "State at Jul 10?" → Version 3 (effective Jul 1)
Query: "State at Jun 15?" → Version 2 (effective Jun 1)4.4 Immutability Guarantees
| Component | Immutability Rule |
|---|---|
| Prior versions | Never modified after creation |
| Archived clauses | Never recalculated; final_computed_state frozen |
| Version chain | Append-only; versions never deleted |
| Amendment records | Immutable audit trail |
5: Amendment Processing
5.1 Data Change Processing
When deal or clause data changes (settlements, events, external data):
1. CAPTURE CHANGE
- Identify changed fields
- Record change metadata (who, when, what)
2. CREATE NEW VERSION
- Increment version number
- Set effective_date
- Copy current structure
- Apply data changes
3. FULL RECALCULATION
- Evaluate all active clauses
- Process for_each loops with new data
- Evaluate all events
- Compute all outputs
- Aggregate at deal level
4. STORE VERSION
- Store complete version snapshot
- Update instance_metadata.current_versionExample: Show Settlement
// Version 2 created when Show 1 settles
{
"version_info": {
"version": 2,
"effective_date": "2024-06-01",
"prior_version": 1,
"change_type": "data_update",
"change_summary": "Show 1 (MSG) settlement: gross $500,000, expenses $75,000"
}
}5.2 Logic Amendment Processing
When clause or deal logic is modified:
1. VALIDATE AMENDMENT
- Verify authorization
- Validate new logic structure
- Check for circular dependencies
2. CREATE NEW VERSION
- Increment version number
- Set effective_date (may be backdated)
- Record amendment details
- Apply logic changes
3. FULL RECALCULATION FROM INCEPTION
- Recalculate entire deal with new logic
- Apply new logic to ALL historical data
- Recompute all events and outputs
NOTE: This ensures the deal reflects "what would have been calculated
if this logic had always been in place." Actual cash movements are
handled by downstream accounting systems.
4. STORE VERSION
- Store complete version snapshot
- Update instance_metadata.current_versionExample: Percentage Change
// Artist percentage changed from 85% to 87.5%
{
"version_info": {
"version": 5,
"effective_date": "2024-07-01",
"prior_version": 4,
"change_type": "logic_amendment",
"change_summary": "Artist percentage increased from 85% to 87.5%",
"amendment": {
"amendment_id": "AMD-001",
"reason": "Renegotiated split based on ticket sales performance",
"document_ref": "contracts/deal-2024-001234/amendment-001.pdf",
"authorized_by": "agent-jane-doe",
"changes": [
{
"action": "modify_logic",
"clause_id": "show_settlement",
"field": "artist_percentage",
"old_value": 0.85,
"new_value": 0.875
}
]
}
}
}5.3 Clause Replacement Processing
When a clause is replaced by a new version:
1. VALIDATE REPLACEMENT
- Verify authorization
- Validate new clause structure
- Check output compatibility (if other clauses depend on outputs)
2. ARCHIVE OLD CLAUSE
- Set status = "superseded"
- Set effective_until = amendment effective_date
- Set superseded_by = new clause_id
- Capture final_computed_state (frozen, never recalculated)
- Move to archived_clauses
3. ADD NEW CLAUSE
- Set status = "active"
- Set effective_from = amendment effective_date
- Set replaces = old clause_id
- Copy logic from clause type
4. UPDATE CROSS-CLAUSE REFERENCES
- Update deal_logic references to point to new clause
- Update any clause references (if applicable)
5. CREATE NEW VERSION
- Increment version number
- Record amendment details
- Include both archived and new clauses
6. FULL RECALCULATION
- Recalculate all ACTIVE clauses only
- Archived clauses retain frozen final_computed_state
- Aggregate at deal level using active clause outputs
7. STORE VERSIONExample: Bonus Structure Replacement
{
"version_info": {
"version": 4,
"effective_date": "2024-07-15",
"change_type": "clause_replacement",
"change_summary": "Replaced tiered bonus with simplified flat bonus",
"amendment": {
"amendment_id": "AMD-002",
"changes": [
{
"action": "deactivate",
"clause_id": "bonus_structure_v1",
"reason": "Superseded by simplified structure"
},
{
"action": "add",
"clause_id": "bonus_structure_v2",
"clause_type_ref": { "id": "flat-bonus", "version": "1.0.0" },
"replaces": "bonus_structure_v1"
}
]
}
},
"archived_clauses": [
{
"clause_id": "bonus_structure_v1",
"status": "superseded",
"effective_until": "2024-07-15",
"superseded_by": "bonus_structure_v2",
"archived_at_version": 4,
"final_computed_state": {
"outputs": {
"total_earned": 25000,
"total_received": 25000
}
}
}
],
"clauses": [
{
"clause_id": "bonus_structure_v2",
"status": "active",
"effective_from": "2024-07-15",
"replaces": "bonus_structure_v1",
"data": { ... },
"logic": { ... }
}
]
}5.4 Clause Removal Processing
When a clause is removed without replacement:
1. ARCHIVE CLAUSE
- Set status = "removed"
- Set effective_until = removal effective_date
- Capture final_computed_state
- Move to archived_clauses
2. UPDATE REFERENCES
- Update deal_logic to handle missing clause outputs
- Use null coalescing (??) for graceful degradation
3. CREATE NEW VERSION & RECALCULATE
- Same as replacement, but no new clause added6: Version Queries
6.1 Current State
To get the current deal state, query the latest version:
GET /deals/{instance_id}/current
Returns: Latest version with computed_state6.2 Specific Version
To get a specific historical version:
GET /deals/{instance_id}/versions/{version}
Returns: Complete version snapshot6.3 Point-in-Time State
To get the state as of a specific date:
GET /deals/{instance_id}/state?as_of=2024-06-15
Returns: Version with latest effective_date ≤ 2024-06-156.4 Version History
To get the amendment history:
GET /deals/{instance_id}/history
Returns: [
{ version: 1, effective_date: "2024-03-15", change_type: "initial", ... },
{ version: 2, effective_date: "2024-06-01", change_type: "data_update", ... },
{ version: 3, effective_date: "2024-06-15", change_type: "data_update", ... },
{ version: 4, effective_date: "2024-07-15", change_type: "clause_replacement", ... }
]6.5 Version Comparison
To compare two versions:
GET /deals/{instance_id}/compare?from=3&to=4
Returns: {
data_changes: [ ... ],
logic_changes: [ ... ],
clause_changes: [
{ action: "deactivate", clause_id: "bonus_structure_v1" },
{ action: "add", clause_id: "bonus_structure_v2" }
],
output_changes: {
total_earned: { from: 150000, to: 175000 },
...
}
}6.6 Archived Clause History
To get the history of a specific clause (including after archival):
GET /deals/{instance_id}/clauses/{clause_id}/history
Returns: {
clause_id: "bonus_structure_v1",
status: "superseded",
active_versions: [1, 2, 3],
archived_at_version: 4,
superseded_by: "bonus_structure_v2",
final_computed_state: { ... }
}7. Cross-Clause Communication
7.1 Output-Based Interface
Clauses communicate through declared outputs only:
Clause: show_settlement
outputs: [total_guarantee, total_earned, total_received, all_settled]
Deal-level logic can reference:
@show_settlement.total_guarantee
@show_settlement.total_earned
@show_settlement.all_settled
Deal-level logic CANNOT reference:
@show_settlement.shows[0].artist_share // Internal data
@show_settlement.net_revenue // Internal computation7.2 Reference Syntax
| Syntax | Meaning | Returns |
|---|---|---|
@clause_id.output | Single clause output | Value or null |
@clause_id[*].output | All instances of clause type | Array of values |
@clause_id.output ?? default | With fallback | Value or default |
7.3 Multi-Instance Clauses
When cardinality is many, multiple clause instances exist:
{
"clauses": [
{ "clause_id": "bonus_group_1", "clause_type_ref": { "id": "tiered-bonus" }, ... },
{ "clause_id": "bonus_group_2", "clause_type_ref": { "id": "tiered-bonus" }, ... },
{ "clause_id": "bonus_group_3", "clause_type_ref": { "id": "tiered-bonus" }, ... }
]
}Referencing multiple instances:
// Reference specific instance
@bonus_group_1.earned
// Reference all instances of a clause type
sum(@tiered-bonus[*].earned ?? 0)
// Alternative: reference by clause_id pattern
sum(@bonus_group_*.earned ?? 0)7.4 Cross-Clause Dependencies
Cross-clause references create dependencies:
tour_versus depends on show_settlement
(tour_versus.versus_amount uses @show_settlement.total_earned)Evaluation order respects dependencies:
show_settlementevaluated firsttour_versusevaluated second (can now reference show_settlement outputs)
Circular cross-clause dependencies are invalid.
8. Schedule Integration
8.1 Embedded Schedules
Schedules are embedded in clause data, not referenced externally:
{
"data": {
"earning_schedule": {
"pattern": "straight_line",
"start_date": "2024-03-01",
"end_date": "2024-09-30"
},
"receipt_schedule": {
"pattern": "equal_periodic_installments",
"total_amount": 3100000,
"frequency": "quarterly",
"period_count": 4,
"start_date": "2024-03-01"
}
}
}8.2 Schedule Patterns
Earning schedules (when revenue is recognized):
| Pattern | Description |
|---|---|
straight_line | Linear recognition over period |
event_triggered | Recognized when event occurs |
periodic | Recognized at regular intervals |
Receipt schedules (when cash is received):
| Pattern | Description |
|---|---|
equal_periodic_installments | Equal amounts at regular intervals |
event_installments | Specific amounts at specific events |
8.3 Schedule Computation
Schedules produce computed values:
{
"receipt_schedule": {
"pattern": "equal_periodic_installments",
"total_amount": 400000,
"frequency": "quarterly",
"period_count": 4,
"start_date": "2024-01-01"
}
}Computed schedule state:
{
"computed_schedule": {
"installments": [
{ "date": "2024-01-01", "amount": 100000, "status": "received" },
{ "date": "2024-04-01", "amount": 100000, "status": "received" },
{ "date": "2024-07-01", "amount": 100000, "status": "pending" },
{ "date": "2024-10-01", "amount": 100000, "status": "future" }
],
"total_received": 200000,
"total_pending": 100000,
"total_future": 100000
}
}9. Validation Rules
9.1 Instance Structure Validation
| Rule | Description |
|---|---|
| DI-1 | instance_id must be unique |
| DI-2 | instance_metadata required |
| DI-3 | deal_data must match deal type schema |
| DI-4 | Each clause must have unique clause_id |
9.2 Clause Instance Validation
| Rule | Description |
|---|---|
| CI-1 | clause_id must be unique within instance |
| CI-2 | category must be valid |
| CI-3 | value_type required for financial clauses |
| CI-4 | data must match clause type schema |
| CI-5 | logic must be present (copied from clause type) |
9.3 Logic Validation
| Rule | Description |
|---|---|
| LV-1 | No circular dependencies within clause |
| LV-2 | No circular dependencies across clauses |
| LV-3 | All output references must target declared outputs |
| LV-4 | All collection references must exist in data |
9.4 Computation Validation
| Rule | Description |
|---|---|
| CV-1 | All variables must be defined before use |
| CV-2 | Type consistency in expressions |
| CV-3 | Collection operations only on arrays |
| CV-4 | Computed fields must exist in schema |
9.5 Version Validation
| Rule | Description |
|---|---|
| VR-1 | version must be a positive integer |
| VR-2 | version must increment sequentially (no gaps) |
| VR-3 | prior_version must reference an existing version |
| VR-4 | Version 1 must have prior_version: null |
| VR-5 | effective_date must be ≥ prior version's effective_date |
| VR-6 | change_type must be a valid change type |
| VR-7 | amendment required for non-data change types |
9.6 Clause Status Validation
| Rule | Description |
|---|---|
| CS-1 | Active clauses must have effective_until: null |
| CS-2 | Superseded clauses must have effective_until set |
| CS-3 | Superseded clauses must have superseded_by set |
| CS-4 | Replacement clauses must have replaces referencing archived clause |
| CS-5 | effective_from must be ≤ effective_until (when both set) |
9.7 Archive Validation
| Rule | Description |
|---|---|
| AR-1 | Archived clauses must have final_computed_state |
| AR-2 | Archived clauses must have archived_at_version |
| AR-3 | archived_at_version must be ≤ current version |
| AR-4 | Archived clause final_computed_state must not change |
9.8 Amendment Validation
| Rule | Description |
|---|---|
| AM-1 | Amendment changes must reference valid clause_ids |
| AM-2 | Deactivate actions must target active clauses |
| AM-3 | Add actions must provide valid clause_type_ref |
| AM-4 | Replace references must point to clause being deactivated |
10. Design Rationale
10.1 Why Full Recalculation?
Incremental computation (only recalculating changed values) is complex and error-prone:
- Dependency tracking across
for_eachloops - Cascading updates through cross-clause references
- Risk of stale state
Full recalculation is simpler and safer:
- Always consistent state
- Deterministic results
- Easier to debug and audit
- Modern hardware makes it fast enough for typical deal sizes
10.2 Why Store Logic with Instance?
Storing logic with each instance:
- Immutability: Contract logic never changes unexpectedly
- Reproducibility: Can always re-run exact same logic
- Independence: No catalog dependency at runtime
- Audit: Know exactly what logic produced results
10.3 Why Structured Logic (not Raw DSL)?
Logic is stored as structured JSON (AST-like), not raw DSL text:
- Parseable: No parsing needed at evaluation time
- Versionable: Can detect structural changes
- Transformable: Can optimize, analyze, or migrate
- Portable: Language-agnostic representation
10.4 Why Separate Computed State?
Computed state is separated from input data:
- Clarity: Clear what's input vs. derived
- Recomputation: Easy to throw away and recalculate
- Versioning: Can store computation history
- Debugging: Compare computed state across versions
10.5 Why Version Every Data Change?
Every data change creates a new version because:
- Complete audit trail: Every state the deal passed through is preserved
- Reproducibility: Can always reconstruct what was known when
- Debugging: Can trace how values evolved over time
- Compliance: Meets audit requirements for financial systems
- Simplicity: One consistent model for all changes
Storage efficiency is handled at the infrastructure level (compression, deduplication), not by complicating the versioning model.
10.6 Why Include Effective Dates?
Effective dates allow:
- Backdated amendments: Legal amendments that apply retroactively
- Point-in-time queries: "What did we think the deal was worth on June 15?"
- Audit alignment: Match versions to business events, not just system timestamps
- Reporting flexibility: Generate reports as of any date
10.7 Why Recalculate from Inception on Logic Changes?
When logic changes, the entire deal is recalculated because:
- Current truth: Deal Engine shows what the deal IS worth under current terms
- Consistency: All computed values reflect consistent logic
- Simplicity: No complex "partial recalculation" logic
- Downstream handling: Accounting systems handle actual cash corrections
The alternative (only recalculating from the effective date forward) creates inconsistent state and complex edge cases.
10.8 Why Archive Replaced Clauses Without Recalculation?
Archived clauses keep their final_computed_state because:
- Historical accuracy: Reflects what actually happened under those terms
- Cash basis: Payments made under old terms remain accurate
- Audit trail: Can verify what was calculated and paid
- No retroactive fiction: Don't pretend old terms never existed
The new clause handles ongoing/future activity; the archived clause preserves history.
11. Relationship to Other Specifications
11.1 Clause Type Specification
Clause types define:
- Schema for clause data (copied to
clause.data) - Logic structure (copied to
clause.logic) - Outputs exposed (referenced via
@clause.output)
11.2 Deal Type Specification
Deal types define:
- Schema for deal data (instantiated to
deal_data) - Suggested clauses (user selects which to include)
- Deal-level logic (copied to
deal_logic)
11.3 This Specification
Deal instances are the runtime result of combining:
- Deal type schema →
deal_data - Selected clause types →
clauses[] - Deal type logic →
deal_logic - Computed results →
computed_state
Document History
- v1.3 (2026-01-XX) - Added instance versioning
- New Section 4: Instance Versioning (version chain, triggers, effective dates)
- New Section 5: Amendment Processing (data changes, logic amendments, clause replacement)
- New Section 6: Version Queries (current, historical, point-in-time, comparison)
- Updated Section 2: Added version_info, clause status fields, archived_clauses
- New validation rules for versioning, clause status, archives, amendments
- Updated design rationale for versioning decisions
- v1.2 (2026-01-XX) - Initial specification
- Complete instance structure
- Computation model (reactive full-recalculation)
- Evaluation order and dependency resolution
- Null handling and event state model
- Cross-clause communication via outputs
- Schedule integration
- Complete example (music touring deal)
- Validation rules
- Design rationale