nx_cgmp.nx source
↩ module page · 236 lines · 9428 B
1// nx_cgmp.nx -- GTM SUITE / 21 CFR 111 cGMP READINESS rung. Deepens the
2// plan engine's coarse "manufacturing / identity" attestations into the real
3// sub-part checklist a dietary-supplement contract manufacturer is audited
4// against -- so the plan can say not "qualify a cGMP facility" but exactly
5// which requirements are missing, worst-first.
6//
7// THE ORDERING IS BY WHAT DRAWS A WARNING LETTER. FDA 483 observations and
8// warning letters cluster on a knowable few requirements: no established
9// specifications, no 100% identity testing of incoming components, no master
10// manufacturing record, no batch record, no quality-control unit. Those are
11// tagged CRITICAL and a facility is not audit-ready while any is open --
12// cgmp_first_critical_gap returns the worst one first, the same kill-order
13// discipline as the production-readiness engine it feeds.
14//
15// EVERY REQUIREMENT IS AN OPERATOR ATTESTATION, HONESTLY. This organ cannot
16// inspect a plant; it tracks which requirements are DECLARED met and scores
17// the gap, weighting CRITICAL > MAJOR > MINOR. It replaces a single yes/no
18// "gmp_qualified" flag with an actionable breakdown, not with a pretense of
19// having audited anything.
20//
21// THE CITATIONS ARE REAL so a plan line points at the actual regulation.
22//
23// Grounding (cited; researcher-groundable):
24// fda_21cfr111_current_good_manufacturing_practice_dietary_supplements
25// 111_70_specifications · 111_75_component_identity_testing
26// 111_205_master_manufacturing_record · 111_255_batch_production_record
27// 111_65_quality_control_operations · fda_483_warning_letter_cgmp_patterns
28//
29// genealogy_id: supplement_gtm + service_infrastructure
30
31import "nx_syscalls.nx"
32
33// ===== Criticality (sealed) ===========================================
34
35const CG_MINOR: i64 = 1
36const CG_MAJOR: i64 = 2
37const CG_CRITICAL: i64 = 3
38
39// ===== The 21 CFR 111 requirement catalog =============================
40
41const CG_SPECIFICATIONS: i64 = 0 // 111.70 established specs
42const CG_ID_TESTING: i64 = 1 // 111.75(a)(1)(i) 100% component identity
43const CG_SUPPLIER_QUAL: i64 = 2 // 111.75(a)(2) supplier qualification
44const CG_FINISHED_TESTING: i64 = 3 // 111.75(c) finished-product to spec
45const CG_MMR: i64 = 4 // 111.205 master manufacturing record
46const CG_BPR: i64 = 5 // 111.255 batch production record
47const CG_QC_UNIT: i64 = 6 // 111.65 quality-control operations
48const CG_RESERVE_SAMPLES: i64 = 7 // 111.83 reserve samples
49const CG_TRAINED_PERSONNEL: i64 = 8 // 111.13 trained personnel
50const CG_SANITARY_PLANT: i64 = 9 // 111.15 sanitary physical plant
51const CG_CALIBRATED_EQUIP: i64 = 10 // 111.27 calibrated equipment
52const CG_LAB_CONTROLS: i64 = 11 // 111.320 laboratory controls
53const CG_COMPLAINTS: i64 = 12 // 111.560 product complaint handling
54const CG_RECORDS: i64 = 13 // 111.605 records retention
55const CG_N: i64 = 14
56
57func cg_valid(id: i64) -> i64 {
58 if id < 0 { return 0 }
59 if id >= CG_N { return 0 }
60 return 1
61}
62
63func cg_name(id: i64) -> *u8 {
64 if id == CG_SPECIFICATIONS { return "established specifications (identity/purity/strength/composition)" as *u8 }
65 if id == CG_ID_TESTING { return "100% identity testing of each incoming dietary ingredient" as *u8 }
66 if id == CG_SUPPLIER_QUAL { return "supplier qualification for other components" as *u8 }
67 if id == CG_FINISHED_TESTING { return "finished-product testing against specification" as *u8 }
68 if id == CG_MMR { return "master manufacturing record (MMR)" as *u8 }
69 if id == CG_BPR { return "batch production record (BPR) per batch" as *u8 }
70 if id == CG_QC_UNIT { return "quality-control unit with approval/reject authority" as *u8 }
71 if id == CG_RESERVE_SAMPLES { return "reserve samples of each batch" as *u8 }
72 if id == CG_TRAINED_PERSONNEL { return "trained, qualified personnel" as *u8 }
73 if id == CG_SANITARY_PLANT { return "sanitary physical plant + pest control" as *u8 }
74 if id == CG_CALIBRATED_EQUIP { return "calibrated, maintained equipment" as *u8 }
75 if id == CG_LAB_CONTROLS { return "laboratory controls + scientifically valid methods" as *u8 }
76 if id == CG_COMPLAINTS { return "product complaint handling procedure" as *u8 }
77 if id == CG_RECORDS { return "records retained >= 1 yr past shelf life + 2 yr" as *u8 }
78 return "unknown requirement" as *u8
79}
80
81func cg_citation(id: i64) -> *u8 {
82 if id == CG_SPECIFICATIONS { return "21 CFR 111.70" as *u8 }
83 if id == CG_ID_TESTING { return "21 CFR 111.75(a)(1)(i)" as *u8 }
84 if id == CG_SUPPLIER_QUAL { return "21 CFR 111.75(a)(2)" as *u8 }
85 if id == CG_FINISHED_TESTING { return "21 CFR 111.75(c)" as *u8 }
86 if id == CG_MMR { return "21 CFR 111.205" as *u8 }
87 if id == CG_BPR { return "21 CFR 111.255" as *u8 }
88 if id == CG_QC_UNIT { return "21 CFR 111.65" as *u8 }
89 if id == CG_RESERVE_SAMPLES { return "21 CFR 111.83" as *u8 }
90 if id == CG_TRAINED_PERSONNEL { return "21 CFR 111.13" as *u8 }
91 if id == CG_SANITARY_PLANT { return "21 CFR 111.15" as *u8 }
92 if id == CG_CALIBRATED_EQUIP { return "21 CFR 111.27" as *u8 }
93 if id == CG_LAB_CONTROLS { return "21 CFR 111.320" as *u8 }
94 if id == CG_COMPLAINTS { return "21 CFR 111.560" as *u8 }
95 if id == CG_RECORDS { return "21 CFR 111.605" as *u8 }
96 return "n/a" as *u8
97}
98
99// Criticality: the CRITICAL set is the warning-letter cluster.
100func cg_criticality(id: i64) -> i64 {
101 if id == CG_SPECIFICATIONS { return CG_CRITICAL }
102 if id == CG_ID_TESTING { return CG_CRITICAL }
103 if id == CG_FINISHED_TESTING { return CG_CRITICAL }
104 if id == CG_MMR { return CG_CRITICAL }
105 if id == CG_BPR { return CG_CRITICAL }
106 if id == CG_QC_UNIT { return CG_CRITICAL }
107 if id == CG_SUPPLIER_QUAL { return CG_MAJOR }
108 if id == CG_RESERVE_SAMPLES { return CG_MAJOR }
109 if id == CG_TRAINED_PERSONNEL { return CG_MAJOR }
110 if id == CG_SANITARY_PLANT { return CG_MAJOR }
111 if id == CG_CALIBRATED_EQUIP { return CG_MAJOR }
112 if id == CG_LAB_CONTROLS { return CG_MAJOR }
113 if id == CG_COMPLAINTS { return CG_MINOR }
114 if id == CG_RECORDS { return CG_MINOR }
115 return 0
116}
117
118// ===== State: which requirements are met ==============================
119//
120// A bitmask, so a facility's whole cGMP posture is one integer.
121
122struct NxCgmpState {
123 met_mask: i64,
124}
125
126func nx_cgmp_new() -> *NxCgmpState {
127 let s: *NxCgmpState = (sys_mmap(16)) as *NxCgmpState
128 s.met_mask = 0
129 return s
130}
131
132func cg_set_met(s: *NxCgmpState, id: i64) -> i64 {
133 if cg_valid(id) != 1 { return 0 }
134 let bit: i64 = 1 << id
135 s.met_mask = s.met_mask | bit
136 return 1
137}
138
139func cg_is_met(s: *NxCgmpState, id: i64) -> i64 {
140 if cg_valid(id) != 1 { return 0 }
141 let bit: i64 = 1 << id
142 let m: i64 = s.met_mask & bit
143 if m != 0 { return 1 }
144 return 0
145}
146
147// Set every requirement met -- an audit-ready facility, for round-tripping.
148func cg_set_all(s: *NxCgmpState) -> i64 {
149 var i: i64 = 0
150 while i < CG_N {
151 cg_set_met(s, i)
152 i = i + 1
153 }
154 return 0
155}
156
157// ===== Gap analysis ===================================================
158
159// The first UNMET CRITICAL requirement (id order), or -1 if all critical met.
160// This is the "fix this before you even schedule the audit" answer.
161func cg_first_critical_gap(s: *NxCgmpState) -> i64 {
162 var i: i64 = 0
163 var gap: i64 = 0 - 1
164 while i < CG_N {
165 if gap < 0 {
166 if cg_criticality(i) == CG_CRITICAL {
167 if cg_is_met(s, i) != 1 { gap = i }
168 }
169 }
170 i = i + 1
171 }
172 return gap
173}
174
175func cg_critical_gaps(s: *NxCgmpState) -> i64 {
176 var i: i64 = 0
177 var n: i64 = 0
178 while i < CG_N {
179 if cg_criticality(i) == CG_CRITICAL {
180 if cg_is_met(s, i) != 1 { n = n + 1 }
181 }
182 i = i + 1
183 }
184 return n
185}
186
187func cg_total_gaps(s: *NxCgmpState) -> i64 {
188 var i: i64 = 0
189 var n: i64 = 0
190 while i < CG_N {
191 if cg_is_met(s, i) != 1 { n = n + 1 }
192 i = i + 1
193 }
194 return n
195}
196
197// A facility is audit-ready only when EVERY critical requirement is met. A
198// facility with critical gaps is not "nearly ready" -- it is not ready.
199func cg_audit_ready(s: *NxCgmpState) -> i64 {
200 if cg_first_critical_gap(s) < 0 { return 1 }
201 return 0
202}
203
204// Weighted readiness, per-mil. CRITICAL requirements carry the most weight, so
205// the score reflects audit risk, not a raw count.
206func cg_total_weight() -> i64 {
207 var i: i64 = 0
208 var w: i64 = 0
209 while i < CG_N {
210 w = w + cg_criticality(i)
211 i = i + 1
212 }
213 return w
214}
215
216func cg_readiness_permil(s: *NxCgmpState) -> i64 {
217 let total: i64 = cg_total_weight()
218 if total <= 0 { return 0 }
219 var i: i64 = 0
220 var got: i64 = 0
221 while i < CG_N {
222 if cg_is_met(s, i) == 1 { got = got + cg_criticality(i) }
223 i = i + 1
224 }
225 return got * 1000 / total
226}
227
228// The two requirements the production-plan's IDENTITY dimension maps to:
229// established specs + 100% component identity testing. Exposed so the plan's
230// coarse "identity_ready" flag can be DERIVED from the real requirements
231// rather than asserted.
232func cg_identity_dimension_met(s: *NxCgmpState) -> i64 {
233 if cg_is_met(s, CG_SPECIFICATIONS) != 1 { return 0 }
234 if cg_is_met(s, CG_ID_TESTING) != 1 { return 0 }
235 return 1
236}