Skip to main content

qualia_core_db/governance/
illocution.rs

1//! Illocutionary force + binding-weight conflict resolution (modal-junctures.n3, in code).
2//!
3//! Answers: when norms conflict on the same (party, action), how does a SOFT directive
4//! (Recommends/Urges) resolve against a HARD commissive (Undertakes) or prohibitive
5//! (Forbids)? By **binding-weight precedence**, with two refinements drawn from the
6//! taxonomy:
7//!   * a directive's weight is first scaled by the SPEAKER'S AUTHORITY (a UN treaty
8//!     body's "calls upon" outweighs an NGO's — the authority variable);
9//!   * an EXEMPTIVE (derogation / waiver — the `q42:unless` defeater) OVERRIDES an
10//!     otherwise-active obligation/prohibition regardless of weight;
11//!   * equal effective weight is a GENUINE conflict, held PARACONSISTENTLY (both norms
12//!     retained for adjudication — the engine does not crash or silently pick one).
13
14/// The engine state a juncture triggers (see modal-junctures.n3 mj:EngineState).
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum EngineState {
17    EpistemicAbsolute,
18    EpistemicWeighted,
19    ContextualAnchor,
20    ObligateSelf,
21    ObligateSelfLiability,
22    ObligateTarget,
23    Recommend,
24    QueueAwait,
25    Instantiate,
26    DefeatExpire,
27    ConceptBinding,
28    Permit,
29    DefeaterUnless,
30    Forbid,
31    ForbidTemporal,
32    AlignmentPositive,
33    AlignmentNegative,
34}
35
36/// Deontic binding weight (0 = no binding .. 255 = hardest). Mirrors mj:bindingWeight.
37pub const W_NONE: u8 = 0; // assertive / expressive / declarative-performative
38pub const W_REQUEST: u8 = 30; // rogative directive (queue/await)
39pub const W_RECOMMEND: u8 = 50; // exhortative directive (soft)
40pub const W_PERMIT: u8 = 128; // permissive authoritative
41pub const W_DIRECTIVE_HARD: u8 = 160; // imperative directive (pre-authority-scaling)
42pub const W_OBLIGATE: u8 = 200; // commissive promissive / prohibitive interdictive
43pub const W_GUARANTEE: u8 = 220; // commissive guarantive (+liability)
44pub const W_EXEMPT: u8 = 250; // permissive exemptive (derogation/waiver → defeater)
45
46/// True if this engine state is an Exemptive defeater (derogation / waiver / `q42:unless`).
47#[inline]
48pub fn is_exemptive(state: EngineState) -> bool {
49    matches!(state, EngineState::DefeaterUnless)
50}
51
52/// A directive's EFFECTIVE weight scales with the speaker's structural authority (0..255);
53/// non-authority-scaled junctures keep their base weight. So an NGO (low authority) that
54/// "demands" carries far less force than a treaty body that does.
55#[inline]
56pub fn effective_weight(base: u8, authority_scaled: bool, speaker_authority: u8) -> u8 {
57    if authority_scaled {
58        ((base as u16 * speaker_authority as u16) / 255) as u8
59    } else {
60        base
61    }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum Resolution {
66    /// The first norm governs.
67    AGoverns,
68    /// The second norm governs.
69    BGoverns,
70    /// Equal binding force — a genuine conflict, retained paraconsistently for adjudication.
71    GenuineConflict,
72}
73
74/// A norm participating in a conflict: effective binding weight, whether it is an
75/// Exemptive (derogation/waiver), and whether it is IMMUNE — non-derogable, a Hohfeldian
76/// immunity (e.g. ICCPR Art. 4(2): no derogation from the right to life, freedom from
77/// torture, etc.).
78#[derive(Debug, Clone, Copy)]
79pub struct Norm {
80    pub weight: u8,
81    pub exemptive: bool,
82    pub immune: bool,
83}
84
85impl Norm {
86    /// An ordinary norm of the given binding weight.
87    pub fn new(weight: u8) -> Self {
88        Self {
89            weight,
90            exemptive: false,
91            immune: false,
92        }
93    }
94    /// An Exemptive (derogation / waiver → the q42:unless defeater).
95    pub fn exemptive(weight: u8) -> Self {
96        Self {
97            weight,
98            exemptive: true,
99            immune: false,
100        }
101    }
102    /// A non-derogable norm (Hohfeldian immunity) — cannot be defeated by an Exemptive.
103    pub fn immune(weight: u8) -> Self {
104        Self {
105            weight,
106            exemptive: false,
107            immune: true,
108        }
109    }
110}
111
112/// Resolve two CONFLICTING norms (same party + action): the Exemptive override (BOUNDED
113/// by immunity), then effective binding weight.
114pub fn resolve_conflict(a: Norm, b: Norm) -> Resolution {
115    // An Exemptive defeats the other norm regardless of weight — UNLESS that norm is
116    // non-derogable (immune), in which case the derogation is invalid and the immune norm
117    // STANDS (ICCPR Art. 4(2): the right to life etc. cannot be derogated).
118    match (a.exemptive, b.exemptive) {
119        (true, false) => {
120            return if b.immune {
121                Resolution::BGoverns
122            } else {
123                Resolution::AGoverns
124            };
125        }
126        (false, true) => {
127            return if a.immune {
128                Resolution::AGoverns
129            } else {
130                Resolution::BGoverns
131            };
132        }
133        _ => {}
134    }
135    use std::cmp::Ordering::*;
136    match a.weight.cmp(&b.weight) {
137        Greater => Resolution::AGoverns,
138        Less => Resolution::BGoverns,
139        Equal => Resolution::GenuineConflict,
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn soft_directive_never_overrides_a_hard_commissive() {
149        // "Recommends X" vs "Undertakes not-X" on the same party+action.
150        let recommend = Norm::new(W_RECOMMEND);
151        let undertake = Norm::new(W_OBLIGATE);
152        assert_eq!(resolve_conflict(undertake, recommend), Resolution::AGoverns);
153        assert_eq!(resolve_conflict(recommend, undertake), Resolution::BGoverns);
154    }
155
156    #[test]
157    fn exemptive_overrides_a_derogable_obligation() {
158        // A derogation/waiver (Exemptive → q42:unless) defeats a DEROGABLE obligation.
159        let derogation = Norm::exemptive(W_EXEMPT);
160        let obligation = Norm::new(W_OBLIGATE);
161        assert_eq!(
162            resolve_conflict(derogation, obligation),
163            Resolution::AGoverns
164        );
165        assert_eq!(
166            resolve_conflict(obligation, derogation),
167            Resolution::BGoverns
168        );
169    }
170
171    #[test]
172    fn exemptive_cannot_defeat_a_non_derogable_norm() {
173        // ICCPR Art. 4 derogation (Exemptive) vs Art. 6 right to life — non-derogable per
174        // Art. 4(2). The immune norm STANDS; the derogation is invalid against it.
175        let derogation = Norm::exemptive(W_EXEMPT);
176        let right_to_life = Norm::immune(W_OBLIGATE);
177        assert_eq!(
178            resolve_conflict(derogation, right_to_life),
179            Resolution::BGoverns
180        );
181        assert_eq!(
182            resolve_conflict(right_to_life, derogation),
183            Resolution::AGoverns
184        );
185    }
186
187    #[test]
188    fn equal_force_is_a_genuine_paraconsistent_conflict() {
189        // e.g. an Obligate and a Forbid of equal weight on the same act — held, not crashed.
190        assert_eq!(
191            resolve_conflict(Norm::new(W_OBLIGATE), Norm::new(W_OBLIGATE)),
192            Resolution::GenuineConflict
193        );
194    }
195
196    #[test]
197    fn directive_weight_scales_with_speaker_authority() {
198        // The SAME imperative "demands" carries different force by who says it.
199        let ngo = effective_weight(W_DIRECTIVE_HARD, true, 30); // low authority
200        let treaty_body = effective_weight(W_DIRECTIVE_HARD, true, 255); // high authority
201        assert!(ngo < treaty_body);
202        // An NGO's demand loses to a hard commitment; the treaty body's competes/wins.
203        assert_eq!(
204            resolve_conflict(Norm::new(ngo), Norm::new(W_OBLIGATE)),
205            Resolution::BGoverns
206        );
207        assert_eq!(
208            resolve_conflict(Norm::new(treaty_body), Norm::new(W_RECOMMEND)),
209            Resolution::AGoverns
210        );
211    }
212}