code wiki / (root) / nx_privlog_lib.nx

nx_privlog_lib.nx source

↩ module page · 154 lines · 7002 B

1// nx_privlog_lib.nx -- PRIVILEGE: the assertion test, the waiver test, and the FRCP 26(b)(5)(A) log. 2// nx_ediscovery decides what may be PRODUCED (allow-list) and nx_redact decides what must be MASKED. 3// Neither answers the question that actually loses privilege: for each document we WITHHOLD, is the 4// claim sound, and have we described it well enough that opposing counsel can test it? Withholding 5// without an adequate log is itself a route to waiver -- the document stays back, and the privilege 6// goes anyway. 7// 8// ATTORNEY-CLIENT (all five, no exceptions): it must be a COMMUNICATION, between ATTORNEY AND CLIENT, 9// made FOR LEGAL ADVICE, in CONFIDENCE, and NOT WAIVED. Business advice from a lawyer is not 10// privileged; a lawyer merely copied on a business email is not privileged. Those two mistakes account 11// for an enormous share of real privilege fights, so "for legal advice" is its own required element 12// rather than being implied by the presence of an attorney. 13// 14// WORK PRODUCT (FRCP 26(b)(3)) is a DIFFERENT doctrine with a DIFFERENT strength, and merging the two 15// is a classic error. FACT work product is discoverable on a showing of substantial need AND undue 16// hardship. OPINION work product -- counsel's mental impressions, conclusions, legal theories -- is 17// near-absolute and is NOT surrendered by that showing. Encoded as separate tiers so no combination of 18// need and hardship can ever reach opinion work product. 19// 20// WAIVER + THE FRE 502(b) RESCUE: disclosure to a third party breaks confidentiality and waives -- 21// unless a common-interest relationship covers it, or the disclosure was INADVERTENT and the holder 22// took REASONABLE STEPS to prevent it AND PROMPTLY moved to rectify. All three 502(b) prongs are 23// required; two of three is a waiver. 24// 25// STRUCTURE: PURE DECISION CORE (rules, zero I/O) so the doctrine gates independently of storage, per 26// [[feedback-pure-core-not-hostage-to-storage-2026-07-31]]. license_tier: ORIGINAL LIB. 27 28import "nx_matter_lib.nx" 29 30const PL_NONE: i64 = 0 31const PL_FACT_WP: i64 = 1 32const PL_OPINION_WP: i64 = 2 33 34const PL_LOGBUF: i64 = 1024 35 36func pl_is1(v: i64) -> i64 { 37 if v == 1 { return 1 } 38 return 0 39} 40 41// ---- attorney-client privilege: all five elements ---- 42 43func pl_ac_pure(is_comm: i64, atty_client: i64, legal_advice: i64, confidential: i64, waived: i64) -> i64 { 44 if pl_is1(is_comm) == 0 { return 0 } 45 if pl_is1(atty_client) == 0 { return 0 } 46 if pl_is1(legal_advice) == 0 { return 0 } 47 if pl_is1(confidential) == 0 { return 0 } 48 if pl_is1(waived) == 1 { return 0 } 49 return 1 50} 51 52// how many of the five hold -- the drafter-facing deficiency count (waiver counts as a failed element) 53func pl_ac_elements_pure(is_comm: i64, atty_client: i64, legal_advice: i64, confidential: i64, waived: i64) -> i64 { 54 var n: i64 = 0 55 n = n + pl_is1(is_comm) 56 n = n + pl_is1(atty_client) 57 n = n + pl_is1(legal_advice) 58 n = n + pl_is1(confidential) 59 if pl_is1(waived) == 0 { n = n + 1 } 60 return n 61} 62 63// ---- work product: two tiers, deliberately not merged with attorney-client ---- 64 65func pl_wp_tier_pure(anticipation: i64, opinion: i64) -> i64 { 66 if pl_is1(anticipation) == 0 { return PL_NONE } 67 if pl_is1(opinion) == 1 { return PL_OPINION_WP } 68 return PL_FACT_WP 69} 70 71// FACT work product yields to substantial need AND undue hardship. OPINION work product does not 72// yield to any showing -- this function must never return 1 for PL_OPINION_WP. 73func pl_wp_discoverable_pure(tier: i64, substantial_need: i64, undue_hardship: i64) -> i64 { 74 if tier == PL_NONE { return 1 } 75 if tier == PL_OPINION_WP { return 0 } 76 if pl_is1(substantial_need) == 0 { return 0 } 77 if pl_is1(undue_hardship) == 0 { return 0 } 78 return 1 79} 80 81// ---- waiver, with the FRE 502(b) rescue ---- 82 83func pl_waived_pure(disclosed_3p: i64, common_interest: i64, inadvertent: i64, reasonable_steps: i64, prompt_rectify: i64) -> i64 { 84 if pl_is1(disclosed_3p) == 0 { return 0 } 85 if pl_is1(common_interest) == 1 { return 0 } 86 // FRE 502(b): all THREE prongs, or it is a waiver 87 if pl_is1(inadvertent) == 0 { return 1 } 88 if pl_is1(reasonable_steps) == 0 { return 1 } 89 if pl_is1(prompt_rectify) == 0 { return 1 } 90 return 0 91} 92 93// ---- FRCP 26(b)(5)(A): the log entry itself ---- 94 95// six descriptive fields must be present for opposing counsel to assess the claim 96func pl_entry_missing_pure(date: i64, author: i64, recipients: i64, doctype: i64, privtype: i64, description: i64) -> i64 { 97 var miss: i64 = 0 98 if pl_is1(date) == 0 { miss = miss + 1 } 99 if pl_is1(author) == 0 { miss = miss + 1 } 100 if pl_is1(recipients) == 0 { miss = miss + 1 } 101 if pl_is1(doctype) == 0 { miss = miss + 1 } 102 if pl_is1(privtype) == 0 { miss = miss + 1 } 103 if pl_is1(description) == 0 { miss = miss + 1 } 104 return miss 105} 106 107// An entry is adequate only when it is COMPLETE and does NOT itself reveal the privileged content. 108// A description that discloses the very advice it protects defeats the purpose of logging. 109func pl_entry_adequate_pure(missing: i64, reveals_content: i64) -> i64 { 110 if missing != 0 { return 0 } 111 if pl_is1(reveals_content) == 1 { return 0 } 112 return 1 113} 114 115// ---- the log as a whole ---- 116 117// Documents withheld but never logged. Withholding without logging is the silent waiver route, 118// so an unlogged withheld document is counted as a defect, never as a rounding error. 119func pl_unlogged_pure(withheld: i64, logged: i64) -> i64 { 120 if logged >= withheld { return 0 } 121 return withheld - logged 122} 123 124// THE LOG VERDICT: every withheld document logged, and every entry adequate. 125func pl_log_adequate_pure(withheld: i64, logged: i64, inadequate_entries: i64) -> i64 { 126 if withheld < 0 { return 0 } 127 if pl_unlogged_pure(withheld, logged) != 0 { return 0 } 128 if inadequate_entries != 0 { return 0 } 129 return 1 130} 131 132// TOTAL WAIVER EXPOSURE: unlogged withheld documents + inadequate entries. Zero is the only safe number. 133func pl_exposure_pure(withheld: i64, logged: i64, inadequate_entries: i64) -> i64 { 134 return pl_unlogged_pure(withheld, logged) + inadequate_entries 135} 136 137// ---- the withholding decision, composing both doctrines ---- 138 139// A document may be withheld if attorney-client privilege holds OR work product protects it from 140// this particular showing. Neither doctrine alone is assumed to cover the other. 141func pl_may_withhold_pure(ac_privileged: i64, wp_tier: i64, substantial_need: i64, undue_hardship: i64) -> i64 { 142 if pl_is1(ac_privileged) == 1 { return 1 } 143 if wp_tier == PL_NONE { return 0 } 144 if pl_wp_discoverable_pure(wp_tier, substantial_need, undue_hardship) == 0 { return 1 } 145 return 0 146} 147 148func pl_tier_label(t: i64, out: *u8) -> i64 { 149 if t == PL_OPINION_WP { mt_catcopy(out, 0, "OPINION-WP" as *u8); out[10] = 0 as u8; return 10 } 150 if t == PL_FACT_WP { mt_catcopy(out, 0, "FACT-WP" as *u8); out[7] = 0 as u8; return 7 } 151 mt_catcopy(out, 0, "NONE" as *u8) 152 out[4] = 0 as u8 153 return 4 154}