code wiki / (root) / nx_cuad_lib.nx

nx_cuad_lib.nx source

↩ module page · 275 lines · 10555 B

1// nx_cuad_lib.nx -- THE CUAD CLAUSE TAXONOMY (41 types) as a typed, benchmark-aligned playbook. 2// 3// WHY (July 2026 SOTA, researched): CUAD -- 13,000+ expert labels over 510 commercial contracts from 4// EDGAR, covering 41 clause types -- is the benchmark essentially every contract-AI product trains or 5// evaluates against. Our clause layer had a TWO-clause playbook, so "retrieval works" was true and 6// meaningless. This enumerates the real target so coverage becomes a number with a denominator. 7// 8// u2605u2605THE HONESTY MECHANISM, BUILT INTO THE ORGAN: declaring 41 constants would let us claim 41 types of 9// coverage while only a handful can actually be detected -- exactly the coverage-gaming this house has 10// been burned by. So the taxonomy is split in two and the split is enforced in code: 11// DECLARED -- all 41 types exist as named constants (the complete, greppable denominator) 12// CUED -- the subset with real discriminative detection cues 13// cuad_coverage_permille scores against CUED types ONLY. A type that is merely declared can NEVER 14// contribute to a coverage number. The organ literally cannot flatter itself; adding a constant does 15// nothing until you add cues to go with it. 16// 17// u2605THE CONTRADICTION TOOTH: "Uncapped Liability" and "Cap On Liability" are SEPARATE CUAD types, and a 18// contract asserting BOTH is internally inconsistent -- one clause caps exposure while another blows the 19// cap open. Reviewers miss it because the two live pages apart and each looks fine alone. Detected here 20// as a first-class contradiction rather than as two independent findings. 21// 22// u2605FAIL-CLOSED: unrecognised text is CUAD_UNKNOWN, never the nearest type. In diligence, "I could not 23// classify this" is actionable and "probably a notices clause" is how a change-of-control provision 24// gets skipped. 25// 26// license_tier: ORIGINAL LIB. 27 28import "nx_clause_lib.nx" 29 30const CUAD_UNKNOWN: i64 = 0 - 1 31const CUAD_DECLARED: i64 = 41 32 33// ---- the 41 declared types ---- 34const CU_DOCUMENT_NAME: i64 = 0 35const CU_PARTIES: i64 = 1 36const CU_AGREEMENT_DATE: i64 = 2 37const CU_EFFECTIVE_DATE: i64 = 3 38const CU_EXPIRATION_DATE: i64 = 4 39const CU_RENEWAL_TERM: i64 = 5 40const CU_NOTICE_TO_TERMINATE_RENEWAL: i64 = 6 41const CU_GOVERNING_LAW: i64 = 7 42const CU_MOST_FAVORED_NATION: i64 = 8 43const CU_NON_COMPETE: i64 = 9 44const CU_EXCLUSIVITY: i64 = 10 45const CU_NO_SOLICIT_CUSTOMERS: i64 = 11 46const CU_COMPETITIVE_RESTRICTION_EXCEPTION: i64 = 12 47const CU_NO_SOLICIT_EMPLOYEES: i64 = 13 48const CU_NON_DISPARAGEMENT: i64 = 14 49const CU_TERMINATION_FOR_CONVENIENCE: i64 = 15 50const CU_ROFR: i64 = 16 51const CU_CHANGE_OF_CONTROL: i64 = 17 52const CU_ANTI_ASSIGNMENT: i64 = 18 53const CU_REVENUE_PROFIT_SHARING: i64 = 19 54const CU_PRICE_RESTRICTIONS: i64 = 20 55const CU_MINIMUM_COMMITMENT: i64 = 21 56const CU_VOLUME_RESTRICTION: i64 = 22 57const CU_IP_OWNERSHIP_ASSIGNMENT: i64 = 23 58const CU_JOINT_IP_OWNERSHIP: i64 = 24 59const CU_LICENSE_GRANT: i64 = 25 60const CU_NON_TRANSFERABLE_LICENSE: i64 = 26 61const CU_AFFILIATE_LICENSE_LICENSOR: i64 = 27 62const CU_AFFILIATE_LICENSE_LICENSEE: i64 = 28 63const CU_UNLIMITED_LICENSE: i64 = 29 64const CU_PERPETUAL_LICENSE: i64 = 30 65const CU_SOURCE_CODE_ESCROW: i64 = 31 66const CU_POST_TERMINATION_SERVICES: i64 = 32 67const CU_AUDIT_RIGHTS: i64 = 33 68const CU_UNCAPPED_LIABILITY: i64 = 34 69const CU_CAP_ON_LIABILITY: i64 = 35 70const CU_LIQUIDATED_DAMAGES: i64 = 36 71const CU_WARRANTY_DURATION: i64 = 37 72const CU_INSURANCE: i64 = 38 73const CU_COVENANT_NOT_TO_SUE: i64 = 39 74const CU_THIRD_PARTY_BENEFICIARY: i64 = 40 75 76// u2605A type is CUED only if cuad_cue_hit knows how to recognise it. Keep this list and the cue function 77// in lockstep -- adding one without the other is the exact self-flattery this design forbids. 78func cuad_is_cued(t: i64) -> i64 { 79 if t == CU_GOVERNING_LAW { return 1 } 80 if t == CU_MOST_FAVORED_NATION { return 1 } 81 if t == CU_NON_COMPETE { return 1 } 82 if t == CU_EXCLUSIVITY { return 1 } 83 if t == CU_NO_SOLICIT_EMPLOYEES { return 1 } 84 if t == CU_NON_DISPARAGEMENT { return 1 } 85 if t == CU_TERMINATION_FOR_CONVENIENCE { return 1 } 86 if t == CU_ROFR { return 1 } 87 if t == CU_CHANGE_OF_CONTROL { return 1 } 88 if t == CU_ANTI_ASSIGNMENT { return 1 } 89 if t == CU_MINIMUM_COMMITMENT { return 1 } 90 if t == CU_IP_OWNERSHIP_ASSIGNMENT { return 1 } 91 if t == CU_LICENSE_GRANT { return 1 } 92 if t == CU_SOURCE_CODE_ESCROW { return 1 } 93 if t == CU_AUDIT_RIGHTS { return 1 } 94 if t == CU_UNCAPPED_LIABILITY { return 1 } 95 if t == CU_CAP_ON_LIABILITY { return 1 } 96 if t == CU_LIQUIDATED_DAMAGES { return 1 } 97 if t == CU_INSURANCE { return 1 } 98 if t == CU_THIRD_PARTY_BENEFICIARY { return 1 } 99 return 0 100} 101 102func cuad_cued_count() -> i64 { 103 var n: i64 = 0 104 var t: i64 = 0 105 while t < CUAD_DECLARED { 106 n = n + cuad_is_cued(t) 107 t = t + 1 108 } 109 return n 110} 111 112// u2605discriminative cues. Each requires a DISTINCTIVE token, not a common legal word: "escrow" not 113// "agreement", "disparage" not "party". A cue that fires on boilerplate is worse than no cue. 114func cuad_cue_hit(text: *u8, t: i64) -> i64 { 115 if t == CU_GOVERNING_LAW { 116 if cl_has_word(text, "governed" as *u8) == 1 { return 1 } 117 if cl_has_word(text, "jurisdiction" as *u8) == 1 { return 1 } 118 return 0 119 } 120 if t == CU_MOST_FAVORED_NATION { 121 if cl_has_word(text, "favored" as *u8) == 1 { return 1 } 122 if cl_has_word(text, "favoured" as *u8) == 1 { return 1 } 123 return 0 124 } 125 if t == CU_NON_COMPETE { 126 if cl_has_word(text, "compete" as *u8) == 1 { return 1 } 127 if cl_has_word(text, "noncompete" as *u8) == 1 { return 1 } 128 return 0 129 } 130 if t == CU_EXCLUSIVITY { 131 if cl_has_word(text, "exclusive" as *u8) == 1 { return 1 } 132 if cl_has_word(text, "exclusivity" as *u8) == 1 { return 1 } 133 return 0 134 } 135 if t == CU_NO_SOLICIT_EMPLOYEES { 136 if cl_has_word(text, "solicit" as *u8) == 1 { return 1 } 137 return 0 138 } 139 if t == CU_NON_DISPARAGEMENT { 140 if cl_has_word(text, "disparage" as *u8) == 1 { return 1 } 141 if cl_has_word(text, "disparagement" as *u8) == 1 { return 1 } 142 return 0 143 } 144 if t == CU_TERMINATION_FOR_CONVENIENCE { 145 if cl_has_word(text, "convenience" as *u8) == 1 { return 1 } 146 return 0 147 } 148 if t == CU_ROFR { 149 if cl_has_word(text, "refusal" as *u8) == 1 { return 1 } 150 return 0 151 } 152 if t == CU_CHANGE_OF_CONTROL { 153 if cl_has_word(text, "merger" as *u8) == 1 { return 1 } 154 if cl_has_word(text, "acquisition" as *u8) == 1 { return 1 } 155 return 0 156 } 157 if t == CU_ANTI_ASSIGNMENT { 158 if cl_has_word(text, "assign" as *u8) == 1 { return 1 } 159 if cl_has_word(text, "assignment" as *u8) == 1 { return 1 } 160 return 0 161 } 162 if t == CU_MINIMUM_COMMITMENT { 163 if cl_has_word(text, "minimum" as *u8) == 1 { return 1 } 164 return 0 165 } 166 if t == CU_IP_OWNERSHIP_ASSIGNMENT { 167 if cl_has_word(text, "inventions" as *u8) == 1 { return 1 } 168 if cl_has_word(text, "workmade" as *u8) == 1 { return 1 } 169 return 0 170 } 171 if t == CU_LICENSE_GRANT { 172 if cl_has_word(text, "license" as *u8) == 1 { return 1 } 173 if cl_has_word(text, "licence" as *u8) == 1 { return 1 } 174 return 0 175 } 176 if t == CU_SOURCE_CODE_ESCROW { 177 if cl_has_word(text, "escrow" as *u8) == 1 { return 1 } 178 return 0 179 } 180 if t == CU_AUDIT_RIGHTS { 181 if cl_has_word(text, "audit" as *u8) == 1 { return 1 } 182 return 0 183 } 184 if t == CU_UNCAPPED_LIABILITY { 185 if cl_has_word(text, "unlimited" as *u8) == 1 { return 1 } 186 if cl_has_word(text, "uncapped" as *u8) == 1 { return 1 } 187 return 0 188 } 189 if t == CU_CAP_ON_LIABILITY { 190 if cl_has_word(text, "capped" as *u8) == 1 { return 1 } 191 if cl_has_word(text, "aggregate" as *u8) == 1 { return 1 } 192 return 0 193 } 194 if t == CU_LIQUIDATED_DAMAGES { 195 if cl_has_word(text, "liquidated" as *u8) == 1 { return 1 } 196 return 0 197 } 198 if t == CU_INSURANCE { 199 if cl_has_word(text, "insurance" as *u8) == 1 { return 1 } 200 if cl_has_word(text, "insured" as *u8) == 1 { return 1 } 201 return 0 202 } 203 if t == CU_THIRD_PARTY_BENEFICIARY { 204 if cl_has_word(text, "beneficiary" as *u8) == 1 { return 1 } 205 return 0 206 } 207 return 0 208} 209 210// u2605classify a clause. Returns the FIRST cued type whose cue fires, or CUAD_UNKNOWN. Never guesses. 211func cuad_classify(text: *u8) -> i64 { 212 var t: i64 = 0 213 while t < CUAD_DECLARED { 214 if cuad_is_cued(t) == 1 { 215 if cuad_cue_hit(text, t) == 1 { return t } 216 } 217 t = t + 1 218 } 219 return CUAD_UNKNOWN 220} 221 222// how many CUED types a whole contract text addresses 223func cuad_types_present(text: *u8) -> i64 { 224 var n: i64 = 0 225 var t: i64 = 0 226 while t < CUAD_DECLARED { 227 if cuad_is_cued(t) == 1 { 228 n = n + cuad_cue_hit(text, t) 229 } 230 t = t + 1 231 } 232 return n 233} 234 235// u2605u2605COVERAGE IS SCORED AGAINST CUED TYPES ONLY -- a merely-declared type can never inflate it. 236func cuad_coverage_permille(text: *u8) -> i64 { 237 let c: i64 = cuad_cued_count() 238 if c <= 0 { return 0 } 239 return (cuad_types_present(text) * 1000) / c 240} 241 242// u2605u2605the CONTRADICTION: a contract cannot both cap and uncap the same exposure. 243func cuad_liability_contradiction(text: *u8) -> i64 { 244 if cuad_cue_hit(text, CU_UNCAPPED_LIABILITY) == 0 { return 0 } 245 if cuad_cue_hit(text, CU_CAP_ON_LIABILITY) == 0 { return 0 } 246 return 1 247} 248 249// u2605the diligence high-risk subset: types that change the economics or control of the deal. 250func cuad_is_high_risk(t: i64) -> i64 { 251 if t == CU_UNCAPPED_LIABILITY { return 1 } 252 if t == CU_MOST_FAVORED_NATION { return 1 } 253 if t == CU_CHANGE_OF_CONTROL { return 1 } 254 if t == CU_NON_COMPETE { return 1 } 255 if t == CU_IP_OWNERSHIP_ASSIGNMENT { return 1 } 256 if t == CU_EXCLUSIVITY { return 1 } 257 if t == CU_SOURCE_CODE_ESCROW { return 1 } 258 return 0 259} 260 261func cuad_high_risk_present(text: *u8) -> i64 { 262 var n: i64 = 0 263 var t: i64 = 0 264 while t < CUAD_DECLARED { 265 if cuad_is_cued(t) == 1 { 266 if cuad_is_high_risk(t) == 1 { n = n + cuad_cue_hit(text, t) } 267 } 268 t = t + 1 269 } 270 return n 271} 272 273// honest self-report: declared vs cued, so a caller can never mistake the denominator. 274func cuad_declared_count() -> i64 { return CUAD_DECLARED } 275func cuad_uncued_count() -> i64 { return CUAD_DECLARED - cuad_cued_count() }