Skip to main content

Module deontic

Module deontic 

Source
Expand description

Deontic Logic extension for the Qualia Bytecode VM.

Implements a defeasible deontic contract evaluator over a &[NQuin] slice. Conforms to the 42 MB Prolog Sentinel memory ceiling, the 48-byte Super-Quin invariant, and the zero-heap-allocation mandate (no Vec, String, or Box).

§Opcodes

Three raw u8 constants define the deontic modality, packed into bits 0–7 of the predicate field of every norm Quin:

ConstantValueSDL formulaMeaning
OP_OBLIGATE0x10O(φ)Party must perform action φ
OP_PERMIT0x11P(φ)Party may perform action φ
OP_FORBID0x12F(φ)=O(¬φ)Party must not perform action φ

§48-byte Norm Quin Layout

┌──────────┬──────────────────────────────────────────────────────────────────┐
│ Field    │ Bit layout                                                       │
├──────────┼──────────────────────────────────────────────────────────────────┤
│ subject  │ [63]=0 (rsvd)  │ [0..62] = FNV-1a hash of the bound party DID  │
│ predicate│ [63]=DEFEATER  │ [8..62] = property-path hash (action/norm URI) │
│          │                │ [0..7]  = deontic opcode (OP_OBLIGATE etc.)    │
│ object   │ [63]=0 (rsvd)  │ [0..62] = FNV-1a hash of the action object    │
│ context  │ [56..63] = sensitivity class (from NQuin::SENSITIVITY_*)   │
│          │ [0..55]  = q_hash of the contract/graph DID                     │
│ metadata │ [61..62] = PermissiveRoutingLane bits                           │
│          │ [32..60] = Lamport logical clock                                │
│          │ [0..31]  = expiry as truncated Unix-32 timestamp                │
│ parity   │ XOR fold of subject ⊕ predicate ⊕ object ⊕ context (ECC check)  │
└──────────┴──────────────────────────────────────────────────────────────────┘

§Defeater Nodes — q42:unless

A Quin with bit 63 of predicate set (DEFEATER_BIT) is not a primary norm; it is a q42:unless exception node that defeats any norm sharing the same (subject, context, property-path) fingerprint. This supports non-monotonic, defeasible reasoning:

Alice is forbidden from disclosing project data — unless she is speaking to a certified auditor.

The evaluator performs a two-phase linear scan:

  1. Defeater harvest — collect up to MAX_DEFEATER_SLOTS fingerprints into a fixed [u64; 64] stack buffer (512 bytes, one cache-line group on Cortex-A78).
  2. Norm evaluation — for each non-defeater Quin: check expiry, probe the defeater buffer, emit a DeonticVerdict into the caller-supplied out slice.

§Non-Disclosure Agreement (NDA)

NDA between did:web:alice.example and did:web:bob.example, covering confidential project-X data, valid until 2028-01-01 (Unix epoch 1 830 297 600). Three Quins fully encode the agreement and its auditor exception:

// Quin 1 — Alice's confidentiality prohibition
subject   = q_hash("did:web:alice.example")
predicate = OP_FORBID as u64
          | (q_hash("q42:disclose") << 8)           // property-path in [8..62]
object    = q_hash("q42:data:project-x:confidential")
context   = q_hash("did:web:nda:contract-001")      // contract graph
metadata  = 1_830_297_600_u64                        // expiry in bits [0..31]
parity    = subject ^ predicate ^ object ^ context   // ECC fold

// Quin 2 — Bob's symmetric prohibition (identical structure, different subject)
subject   = q_hash("did:web:bob.example")
predicate = OP_FORBID as u64 | (q_hash("q42:disclose") << 8)
object    = q_hash("q42:data:project-x:confidential")
context   = q_hash("did:web:nda:contract-001")
metadata  = 1_830_297_600_u64
parity    = subject ^ predicate ^ object ^ context

// Quin 3 — Defeater: Alice MAY disclose to a certified auditor (q42:unless)
subject   = q_hash("did:web:alice.example")
predicate = DEFEATER_BIT                             // bit 63 marks q42:unless
          | OP_PERMIT as u64
          | (q_hash("q42:disclose") << 8)           // same property-path as Quin 1
object    = q_hash("q42:role:certified-auditor")    // excepted entity class
context   = q_hash("did:web:nda:contract-001")      // same contract graph
metadata  = 1_830_297_600_u64
parity    = subject ^ predicate ^ object ^ context

Quin 3 shares (subject, context, property-path) with Quin 1, so the evaluator marks Quin 1 as DeonticStatus::Defeated when an auditor invokes the exception.

§Guardianship Contract

Ward: did:web:ward.example, Guardian: did:web:guardian.example. Guardianship expires at majority (2030-01-01, epoch 1 893 456 000):

// Quin 1 — Guardian obligated to act in the ward's best interest
subject   = q_hash("did:web:guardian.example")
predicate = OP_OBLIGATE as u64
          | (q_hash("q42:actInBestInterest") << 8)
object    = q_hash("did:web:ward.example")
context   = q_hash("did:web:guardianship:contract-002")
metadata  = 1_893_456_000_u64   // contract expires when ward reaches majority
parity    = subject ^ predicate ^ object ^ context

// Quin 2 — Temporal defeater: ward may self-determine after majority age
subject   = q_hash("did:web:ward.example")
predicate = DEFEATER_BIT
          | OP_PERMIT as u64
          | (q_hash("q42:actInBestInterest") << 8)  // defeats the same obligation path
object    = q_hash("did:web:ward.example")
context   = q_hash("did:web:guardianship:contract-002")
metadata  = 1_893_456_000_u64   // carries same timestamp; expiry semantics applied
parity    = subject ^ predicate ^ object ^ context

After majority, now_unix > 1_893_456_000 causes Quin 1 to emit DeonticStatus::Expired, and the defeater Quin 2 itself becomes moot — demonstrating how temporal bounds compose naturally with defeasibility in a single linear scan without branching stacks.

§Edge-native 3-core CPU triad

The two-phase design maps to the triad naturally:

  • Core 0 — defeater harvest (Phase 1, read-only, highly prefetchable).
  • Core 1 — norm evaluation (Phase 2, linear probe of the 512-byte buffer).
  • Core 2 — verdict dispatch / downstream enforcement routing.

Cache-line pressure is bounded: the [u64; MAX_DEFEATER_SLOTS] buffer fits in 8 × 64-byte cache lines; each DeonticVerdict is 64 bytes (one cache line).

Re-exports§

pub use crate::frame_layout::DEFEATER_BIT;

Structs§

DeonticVerdict
A verdict emitted for one norm Quin. Exactly 64 bytes — one cache line.

Enums§

DefeatKind
How a norm came to be DeonticStatus::Defeated — Hart/Pollock’s rebutting vs undercutting distinction. A rebutting defeater asserts the contrary conclusion (DEFEATER_BIT + an O/P/F opcode); an undercutting defeater (OP_UNDERCUT) severs the rule’s support without asserting the contrary.
DeonticError
DeonticStatus
The result of evaluating a single norm Quin against temporal bounds and defeaters.
NormResolution
The outcome of resolving a norm conflict.

Constants§

MAX_DEFEATER_SLOTS
Stack capacity for defeater fingerprints per evaluation call. 64 slots × 8 bytes = 512 bytes — fits within a single L1 cache-line group.
OP_CONDITIONAL
O(q | p) — the head of a dyadic / conditional obligation: q is obligatory given condition p. Evaluation is fact-driven (see evaluate_conditional_obligation); contrary-to-duty is the special case p = “primary breached”.
OP_FORBID
F(φ) = O(¬φ) — the subject party must not perform the action.
OP_GRATUITOUS
G(φ) — gratuitousness: ¬O(φ). The agent is free to omit φ (it may still be permitted or forbidden). May be asserted or derived (see is_gratuitous).
OP_OBLIGATE
O(φ) — the subject party must perform the action.
OP_OPTIONAL
U(φ) — optionality / indifference: ¬O(φ) ∧ ¬F(φ). The system is indifferent; neither doing nor omitting φ is a violation. May be asserted or derived (see is_optional).
OP_PERMIT
P(φ) — the subject party may perform the action.
OP_STIT
Reserved for Phase 3 (STIT agency): O[α stit φ]. Declared here to fence the opcode so nothing else claims it before agency lands.
OP_UNDERCUT
An undercutting defeater: combined with DEFEATER_BIT it invalidates the inference link p ⇒ Oq without asserting ¬Oq (vs a rebutting defeater — a DEFEATER_BIT node with an O/P/F opcode — which asserts the contrary). The fingerprint match is identical (the opcode byte is masked out); only the classification in DefeatKind differs.

Functions§

compile_n3_rule_to_norm
Compile an N3 [Rule] into a norm Quin (or defeater Quin for ^> rules).
compile_norm_quin
Build a norm Quin from its logical components.
compile_permission_constraint
Compile a permission into a non-fungible cryptographic constraint bound to a specific target nquin: the constraint carries, in context, a BLAKE3 binding to target, so the permission cannot be detached and reused for a different nquin — it travels persistently with that exact one. The identity layer SIGNS this envelope (the engine never holds keys — see meta_deontic::endorsement_credential); this constructs the bound, verifiable constraint.
defeater_fingerprint
Produces the defeater-matching fingerprint for any Quin (norm or defeater).
evaluate_conditional_obligation
General dyadic / conditional obligation O(obligation | condition): the obligation is binding only given the condition holds. Returns true iff the conditional is satisfied — either the condition does not hold (vacuously satisfied), or it holds AND the obligation has been fulfilled.
evaluate_contrary_to_duty
Contrary-to-duty obligation O(reparation / breach): a secondary obligation that arises precisely because a primary obligation was breached (the remedy/reparation logic — Geneva/ICCPR remedy instruments). Returns true iff the CTD is satisfied: either the primary was NOT breached by the party (the CTD is not triggered), or it was breached AND the reparation has been fulfilled.
evaluate_deontic_contract
Evaluate a deontic contract encoded as a &[NQuin] slice.
extract_deontic_opcode
Extracts the deontic opcode from bits [0..7] of a predicate word.
extract_expiry_unix32
Extracts the 32-bit expiry from bits [0..31] of a metadata word. A zero value means “no expiry set” and is always treated as valid.
harvest_defeater_fingerprints
Harvest q42:unless defeater fingerprints from a contract slice (Phase 1 only).
is_gratuitous
True iff action φ is gratuitous (non-obligatory) for party: ¬O(φ) — no active obligation over (party, action) (it may still be permitted or forbidden).
is_optional
True iff action φ is optional / indifferent for party: ¬O(φ) ∧ ¬F(φ) — no active (non-defeater) obligation and no prohibition over (party, action) in the norm slice. (An explicit OP_OPTIONAL assertion also counts.)
norm_has_active_defeater
Returns true if the defeater buffer contains a fingerprint that matches norm.
norm_lifecycle_status
Compute the full lifecycle status of a single norm against an effectivity window, the current time, the harvested defeaters, and a fact slice.
norms_conflict
Do two norms CONFLICT — same party (subject) over the same action (object), with deontically opposed opcodes? Active (non-defeater) norms only.
nquin_binding_hash
A collision-resistant (BLAKE3) fingerprint over a Quin’s six fields — the cryptographic binding anchor. Changing any field changes the fingerprint.
opcodes_conflict
Do two deontic OPCODES conflict — an obligation/permission to do φ vs a prohibition of φ?
permission_binds_to
Verify that a permission constraint is bound to target (the non-fungibility check): its context binding must match target’s current fingerprint. Tampering with target, or moving the constraint to a different nquin, breaks the binding.
resolve_norm_conflict
Resolve a norm conflict by, in strict order:
term_uri_hash