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}