Skip to main content

qualia_core_db/modalities/
contract.rs

1//! Contractual formation & agreement (§22, legal_logic.md) — private ordering.
2//!
3//! Beyond universal human rights, agents create binding private law through agreements. This
4//! formalises the micro-states of formation (Offer → Assent → Binding) and composes **§18
5//! capacity**: mutual assent only creates a binding obligation when *both* parties had the
6//! juridical capacity to agree. A contract may also incorporate a larger normative corpus by
7//! reference (e.g. the UN Guiding Principles).
8
9use crate::modalities::capacity::{stipulation_binding, CapacityStatus};
10
11/// The formation stage of an agreement.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum FormationStage {
14    /// Nothing stipulated yet.
15    #[default]
16    None,
17    /// One party has stipulated an obligation as a condition of engagement (an offer).
18    Offer,
19    /// Both parties have assented — a binding, localised obligation exists.
20    Binding,
21}
22
23/// The raw formation stage from the two acts (capacity aside): an offer needs a stipulation;
24/// binding needs assent on top of it.
25pub fn formation_stage(stipulated: bool, accepted: bool) -> FormationStage {
26    match (stipulated, accepted) {
27        (true, true) => FormationStage::Binding,
28        (true, false) => FormationStage::Offer,
29        _ => FormationStage::None,
30    }
31}
32
33/// A contract is **binding** iff it was stipulated, assented to, AND *both* parties had intact
34/// juridical capacity (composes `capacity::stipulation_binding` — an agreement assented to under
35/// duress or by an incapacitated party does not bind).
36pub fn is_binding_contract(
37    stipulated: bool,
38    accepted: bool,
39    offeror: CapacityStatus,
40    acceptor: CapacityStatus,
41) -> bool {
42    formation_stage(stipulated, accepted) == FormationStage::Binding
43        && stipulation_binding(offeror)
44        && stipulation_binding(acceptor)
45}
46
47/// Incorporation by reference: the agreement imports the clauses of `instrument` (a corpus URI
48/// hash). A non-zero instrument means clauses are incorporated.
49#[inline]
50pub fn incorporates_by_reference(instrument: u64) -> bool {
51    instrument != 0
52}
53
54// ─── Formal verification of terms against deontic / human-rights limits ───────────
55
56/// A contract term that obligates a FORBIDDEN action (a deontic / human-rights limit — e.g. an
57/// agreement to waive a non-derogable right) is VOID. The terms respect the limits iff none of
58/// `obligated_actions` is in `forbidden`. (Private ordering cannot contract out of the baselines.)
59pub fn terms_respect_limits(obligated_actions: &[u64], forbidden: &[u64]) -> bool {
60    obligated_actions.iter().all(|a| !forbidden.contains(a))
61}
62
63// ─── Breach-detection state machine (conditions precedent / subsequent) ───────────
64
65/// The lifecycle state of a binding contract.
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum ContractState {
68    /// A condition PRECEDENT is unmet — the obligation has not yet arisen.
69    Pending,
70    /// In force, awaiting performance.
71    Active,
72    /// Performed — obligation satisfied.
73    Performed,
74    /// A condition SUBSEQUENT occurred — the obligation is terminated/discharged.
75    Discharged,
76    /// The deadline passed without performance — breach.
77    Breached,
78}
79
80/// Contract lifecycle (precedence order): an unmet condition **precedent** → `Pending`; a
81/// condition **subsequent** that occurred → `Discharged`; performance → `Performed`; a passed
82/// deadline without performance → `Breached`; otherwise `Active`.
83pub fn contract_state(
84    precedent_met: bool,
85    subsequent_occurred: bool,
86    performed: bool,
87    deadline_passed: bool,
88) -> ContractState {
89    if !precedent_met {
90        ContractState::Pending
91    } else if subsequent_occurred {
92        ContractState::Discharged
93    } else if performed {
94        ContractState::Performed
95    } else if deadline_passed {
96        ContractState::Breached
97    } else {
98        ContractState::Active
99    }
100}
101
102// ─── Computable performance metrics + oracle ──────────────────────────────────────
103
104/// Performance ratio = `delivered / required` (clamped ≥ 0); `1.0` = fully performed. A
105/// non-positive `required` is vacuously satisfied (`1.0`).
106pub fn performance_ratio(delivered: f64, required: f64) -> f64 {
107    if required <= 0.0 {
108        1.0
109    } else {
110        (delivered / required).max(0.0)
111    }
112}
113
114/// Performance is met iff an **oracle-trusted** measurement shows `delivered` reaching `required`
115/// (`performance_ratio >= 1.0`). An untrusted oracle measurement is not admissible (fail closed).
116pub fn performance_met(delivered: f64, required: f64, oracle_trusted: bool) -> bool {
117    oracle_trusted && performance_ratio(delivered, required) >= 1.0
118}
119
120// ─── Multi-party splitting + sub-contract liability tracing ───────────────────────
121
122/// Trace liability through a sub-contract `chain`: `chain[i]` is the party at depth `i` (the prime
123/// contractor `chain[0]` sub-contracts to `chain[1]`, etc.). Liability for a breach at `depth`
124/// rests on `chain[depth]`, capped at the performing sub-contractor at the chain's end. `None` for
125/// an empty chain.
126pub fn liable_party(chain: &[u64], depth: usize) -> Option<u64> {
127    if chain.is_empty() {
128        None
129    } else {
130        Some(chain[depth.min(chain.len() - 1)])
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn terms_cannot_contract_out_of_baselines() {
140        let waive_dignity = crate::q_hash("act:waiveInherentDignity");
141        let deliver_goods = crate::q_hash("act:deliverGoods");
142        let forbidden = [waive_dignity];
143        assert!(terms_respect_limits(&[deliver_goods], &forbidden));
144        assert!(
145            !terms_respect_limits(&[deliver_goods, waive_dignity], &forbidden),
146            "a void term"
147        );
148        assert!(terms_respect_limits(&[], &forbidden));
149    }
150
151    #[test]
152    fn breach_state_machine() {
153        // Precedent unmet → Pending.
154        assert_eq!(
155            contract_state(false, false, false, true),
156            ContractState::Pending
157        );
158        // In force, nothing yet → Active.
159        assert_eq!(
160            contract_state(true, false, false, false),
161            ContractState::Active
162        );
163        // Performed → Performed.
164        assert_eq!(
165            contract_state(true, false, true, false),
166            ContractState::Performed
167        );
168        // Condition subsequent occurred → Discharged (even if deadline passed).
169        assert_eq!(
170            contract_state(true, true, false, true),
171            ContractState::Discharged
172        );
173        // Deadline passed, no performance, no discharge → Breached.
174        assert_eq!(
175            contract_state(true, false, false, true),
176            ContractState::Breached
177        );
178    }
179
180    #[test]
181    fn performance_metrics_and_oracle() {
182        assert!((performance_ratio(8.0, 10.0) - 0.8).abs() < 1e-9);
183        assert_eq!(performance_ratio(5.0, 0.0), 1.0); // nothing required → satisfied
184        assert!(performance_met(10.0, 10.0, true));
185        assert!(!performance_met(9.9, 10.0, true), "under-delivered");
186        assert!(
187            !performance_met(10.0, 10.0, false),
188            "untrusted oracle → fail closed"
189        );
190    }
191
192    #[test]
193    fn sub_contract_liability_tracing() {
194        let prime = crate::q_hash("party:prime");
195        let sub1 = crate::q_hash("party:sub1");
196        let sub2 = crate::q_hash("party:sub2");
197        let chain = [prime, sub1, sub2];
198        assert_eq!(liable_party(&chain, 0), Some(prime));
199        assert_eq!(liable_party(&chain, 2), Some(sub2));
200        assert_eq!(
201            liable_party(&chain, 9),
202            Some(sub2),
203            "capped at the performing sub-contractor"
204        );
205        assert_eq!(liable_party(&[], 0), None);
206    }
207
208    #[test]
209    fn formation_progresses_offer_to_binding() {
210        assert_eq!(formation_stage(false, false), FormationStage::None);
211        assert_eq!(formation_stage(true, false), FormationStage::Offer);
212        assert_eq!(formation_stage(true, true), FormationStage::Binding);
213        // Acceptance with no offer is not a contract.
214        assert_eq!(formation_stage(false, true), FormationStage::None);
215    }
216
217    #[test]
218    fn binding_requires_capacity_of_both_parties() {
219        let intact = CapacityStatus::Intact;
220        // Full assent + both intact → binding.
221        assert!(is_binding_contract(true, true, intact, intact));
222        // Acceptor under duress → not binding (the agreement is voidable, not binding).
223        assert!(!is_binding_contract(
224            true,
225            true,
226            intact,
227            CapacityStatus::UnderDuress
228        ));
229        // Offeror impaired → not binding.
230        assert!(!is_binding_contract(
231            true,
232            true,
233            CapacityStatus::Impaired,
234            intact
235        ));
236        // Mere offer (no assent) → not binding even with capacity.
237        assert!(!is_binding_contract(true, false, intact, intact));
238    }
239
240    #[test]
241    fn incorporation_by_reference() {
242        assert!(incorporates_by_reference(crate::q_hash("instrument:ungp")));
243        assert!(!incorporates_by_reference(0));
244    }
245}