code wiki / (root) / nx_clm_lib.nx

nx_clm_lib.nx source

↩ module page · 117 lines · 5219 B

1// nx_clm_lib.nx -- F996: CONTRACT LIFECYCLE -- obligation tracking + auto-renewal-trap detection. 2// 3// The expensive, silent contract failure is the EVERGREEN AUTO-RENEWAL TRAP: a contract renews for 4// another (often costly) term unless you give written notice N days before expiry. Miss that window by 5// a day and you are locked in. Legal teams miss it constantly because nothing computes the deadline and 6// nothing flags the day it passes. This organ does both, and it is FAIL-CLOSED: a contract whose expiry 7// or notice period is unknown is flagged FOR REVIEW, never silently reported safe -- because "no action 8// needed" on a contract you could not evaluate is exactly how you get trapped. 9// 10// Dates are integer DAY-NUMBERS (deterministic, no float). Converting a YYYYMMDD to a day-number is the 11// docketing engine's job (nx_docket); this takes day-numbers so the lifecycle math is exact and testable. 12// 13// SCALE ENVELOPE (declared): clm_overdue scans the obligation index for a contract's key-prefix (the 14// proven per-key scan). DRY: composes nx_matter_lib (reg_*/mt_*/canon_encode). license_tier: ORIGINAL LIB. 15 16import "nx_matter_lib.nx" 17const CLM_MAGIC_1048576: i64 = 1048576 18 19const CLM_OPEN: i64 = 0 // notice window still open -- you can still cancel 20const CLM_MISSED: i64 = 1 // past the notice deadline, before expiry -- it WILL auto-renew 21const CLM_RENEWED: i64 = 2 // past expiry -- already auto-renewed into a new term 22const CLM_UNKNOWN: i64 = 0 - 1 // insufficient data -- FLAG FOR REVIEW (fail-closed), never "safe" 23const CLM_COLON: i64 = 58 24 25// the LAST day on which cancellation notice may be given. 26func clm_notice_deadline(expiry_day: i64, notice_days: i64) -> i64 { 27 return expiry_day - notice_days 28} 29 30// ★evaluate the auto-renewal trap as of `today_day`. 31// FAIL-CLOSED: an unknown expiry (<=0) or negative notice period -> CLM_UNKNOWN, not OPEN. 32func clm_renewal_state(expiry_day: i64, notice_days: i64, today_day: i64) -> i64 { 33 if expiry_day <= 0 { return CLM_UNKNOWN } 34 if notice_days < 0 { return CLM_UNKNOWN } 35 let deadline: i64 = expiry_day - notice_days 36 if today_day <= deadline { return CLM_OPEN } 37 if today_day <= expiry_day { return CLM_MISSED } 38 return CLM_RENEWED 39} 40 41// days remaining to give notice; NEGATIVE means the window is already missed. 42func clm_days_to_act(expiry_day: i64, notice_days: i64, today_day: i64) -> i64 { 43 return (expiry_day - notice_days) - today_day 44} 45 46// 1 only if it is still SAFE (window open). Missed, renewed, AND unknown all return 0. 47func clm_is_safe(expiry_day: i64, notice_days: i64, today_day: i64) -> i64 { 48 if clm_renewal_state(expiry_day, notice_days, today_day) == CLM_OPEN { return 1 } 49 return 0 50} 51 52// record a contract obligation with its due day. key = "<contract>:<obl>". 53func clm_obligation_put(prefix: *u8, contract_id: *u8, obl_id: *u8, due_day: i64, otype: *u8) -> i64 { 54 let id: *u8 = sys_mmap(256) 55 var o: i64 = mt_catcopy(id, 0, contract_id) 56 id[o] = CLM_COLON as u8 57 o = o + 1 58 o = mt_catcopy(id, o, obl_id) 59 id[o] = 0 as u8 60 let db: *u8 = sys_mmap(32) 61 var doo: i64 = mt_catn(db, 0, due_day) 62 db[doo] = 0 as u8 63 let k: *i64 = sys_mmap(8 * 2) as *i64 64 let v: *i64 = sys_mmap(8 * 2) as *i64 65 k[0] = ("due" as *u8) as i64 66 v[0] = db as i64 67 k[1] = ("type" as *u8) as i64 68 v[1] = otype as i64 69 let rec: *u8 = sys_mmap(512) 70 let rl: i64 = canon_encode(k, v, 2, rec) 71 return reg_put(prefix, "clm:" as *u8, "clm:__idx__" as *u8, id, rec, rl) 72} 73 74// count a contract's obligations that are OVERDUE as of today_day (due_day < today_day). 75func clm_overdue(prefix: *u8, contract_id: *u8, today_day: i64) -> i64 { 76 let idx: *u8 = sys_mmap(CLM_MAGIC_1048576) 77 let ilen: i64 = reg_index(prefix, "clm:__idx__" as *u8, idx, CLM_MAGIC_1048576) 78 let cp: *u8 = sys_mmap(256) 79 var cpl: i64 = mt_catcopy(cp, 0, contract_id) 80 cp[cpl] = CLM_COLON as u8 81 cpl = cpl + 1 82 cp[cpl] = 0 as u8 83 let idb: *u8 = sys_mmap(256) 84 let po: *i64 = sys_mmap(16) as *i64 85 let lo: *i64 = sys_mmap(16) as *i64 86 let df: *u8 = sys_mmap(32) 87 var overdue: i64 = 0 88 var ls: i64 = 0 89 var i: i64 = 0 90 while i <= ilen { 91 var eol: i64 = 0 92 if i == ilen { eol = 1 } 93 if i < ilen { if idx[i] == 10 as u8 { eol = 1 } } 94 if eol == 1 { 95 if i > ls { 96 var c: i64 = 0 97 while ls + c < i { idb[c] = idx[ls + c]; c = c + 1 } 98 idb[c] = 0 as u8 99 var mtch: i64 = 1 100 var x: i64 = 0 101 while cp[x] != (0 as u8) { if idb[x] != cp[x] { mtch = 0 } x = x + 1 } 102 if mtch == 1 { 103 if reg_get(prefix, "clm:" as *u8, idb, po, lo) == 1 { 104 mt_field(po[0] as *u8, lo[0], "due" as *u8, 3, df) 105 var due: i64 = 0 106 var y: i64 = 0 107 while df[y] != (0 as u8) { due = due * 10 + (df[y] - 48); y = y + 1 } 108 if due < today_day { overdue = overdue + 1 } 109 } 110 } 111 } 112 ls = i + 1 113 } 114 i = i + 1 115 } 116 return overdue 117}