nx_haccp.nx source
↩ module page · 146 lines · 5507 B
1// nx_haccp.nx -- FOOD-SCIENCE SUITE / REGULATORY (HACCP) rung. Turns the
2// safety + preservation science into the HACCP framework: for each process
3// step it names the hazard, decides whether the step is a CRITICAL CONTROL
4// POINT (CCP) or a prerequisite (PRP), sets the grounded CRITICAL LIMIT,
5// monitors an observation against it, and HALTS the batch when a CCP is
6// breached (the never-poison law applied to a whole process).
7//
8// Grounded critical limits (FDA Food Code 2022):
9// cook poultry 74 C (165 F) / ground 71 / whole cut + seafood 63
10// reheat 74 C; hot-hold >= 57 C (135 F); cold-hold <= 5 C (41 F)
11// two-stage cooling 57->21 C in <= 2 h, then 21->5 C in <= 4 h
12// low-acid canning: 12-D botulinum cook (composes nx_preservation)
13// acidification: pH <= 4.6 (composes the ferment never-poison line)
14//
15// THE exceed: a paper HACCP binder lists limits; this MONITORS an
16// observation against the grounded limit and HALTS by construction when a
17// CCP fails -- and it reuses the SAME preservation/ferment science the rest
18// of the suite runs on (one source of truth, not a re-typed chart).
19//
20// genealogy_id: codex_haccp_7_principles + fda_food_code_2022 + nishi_never_poison
21
22import "nx_syscalls.nx"
23import "nx_preservation.nx"
24
25// ===== Sealed enums ===============================================
26
27const HC_RECEIVE: i64 = 0
28const HC_STORE: i64 = 1
29const HC_PREP: i64 = 2
30const HC_COOK: i64 = 3
31const HC_COOL: i64 = 4
32const HC_HOT_HOLD: i64 = 5
33const HC_COLD_HOLD: i64 = 6
34const HC_REHEAT: i64 = 7
35const HC_CAN: i64 = 8
36const HC_ACIDIFY: i64 = 9
37const HC_SERVE: i64 = 10
38const HC_STEP_N: i64 = 11
39
40const HC_HAZ_NONE: i64 = 0
41const HC_HAZ_BIO: i64 = 1 // pathogens
42const HC_HAZ_CHEM: i64 = 2 // allergens, toxins, residues
43const HC_HAZ_PHYS: i64 = 3 // glass, metal, bone
44
45const HC_CCP_NO: i64 = 0 // prerequisite program (PRP)
46const HC_CCP_YES: i64 = 1 // critical control point
47
48const HC_OK: i64 = 0
49const HC_HALT: i64 = 1 // CCP breached -> never-poison halt
50
51// Food classes for cook limits.
52const HC_FOOD_POULTRY: i64 = 0
53const HC_FOOD_GROUND: i64 = 1
54const HC_FOOD_WHOLE: i64 = 2
55const HC_FOOD_SEAFOOD: i64 = 3
56
57// ===== Grounded critical limits ===================================
58
59const HC_COOK_POULTRY_C: i64 = 74
60const HC_COOK_GROUND_C: i64 = 71
61const HC_COOK_WHOLE_C: i64 = 63
62const HC_REHEAT_C: i64 = 74
63const HC_HOT_HOLD_C: i64 = 57
64const HC_COLD_HOLD_C: i64 = 5
65const HC_COOL_S1_HOURS: i64 = 2 // 57 C -> 21 C within 2 h
66const HC_COOL_S2_HOURS: i64 = 4 // 21 C -> 5 C within 4 h
67const HC_ACIDIFY_PH: i64 = 4600
68
69// ===== Hazard analysis + CCP determination ========================
70
71func haccp_primary_hazard(step: i64) -> i64 {
72 if step == HC_RECEIVE { return HC_HAZ_BIO }
73 if step == HC_STORE { return HC_HAZ_BIO }
74 if step == HC_PREP { return HC_HAZ_PHYS }
75 if step == HC_COOK { return HC_HAZ_BIO }
76 if step == HC_COOL { return HC_HAZ_BIO }
77 if step == HC_HOT_HOLD { return HC_HAZ_BIO }
78 if step == HC_COLD_HOLD { return HC_HAZ_BIO }
79 if step == HC_REHEAT { return HC_HAZ_BIO }
80 if step == HC_CAN { return HC_HAZ_BIO }
81 if step == HC_ACIDIFY { return HC_HAZ_BIO }
82 return HC_HAZ_NONE
83}
84
85// A step is a CCP iff a significant hazard is controlled there (cook/cool/
86// hold/reheat/can/acidify); receive/store/prep/serve are prerequisites.
87func haccp_is_ccp(step: i64) -> i64 {
88 if step == HC_COOK { return HC_CCP_YES }
89 if step == HC_COOL { return HC_CCP_YES }
90 if step == HC_HOT_HOLD { return HC_CCP_YES }
91 if step == HC_COLD_HOLD { return HC_CCP_YES }
92 if step == HC_REHEAT { return HC_CCP_YES }
93 if step == HC_CAN { return HC_CCP_YES }
94 if step == HC_ACIDIFY { return HC_CCP_YES }
95 return HC_CCP_NO
96}
97
98// ===== Critical limits + monitoring ===============================
99
100func haccp_cook_limit(food_class: i64) -> i64 {
101 if food_class == HC_FOOD_POULTRY { return HC_COOK_POULTRY_C }
102 if food_class == HC_FOOD_GROUND { return HC_COOK_GROUND_C }
103 if food_class == HC_FOOD_WHOLE { return HC_COOK_WHOLE_C }
104 if food_class == HC_FOOD_SEAFOOD { return HC_COOK_WHOLE_C }
105 return HC_COOK_POULTRY_C // fail-safe: highest limit for unknown
106}
107
108// COOK CCP: observed internal temp must reach the class limit.
109func haccp_cook_admit(observed_c: i64, food_class: i64) -> i64 {
110 if observed_c >= haccp_cook_limit(food_class) { return HC_OK }
111 return HC_HALT
112}
113
114// COOL CCP: two-stage cooling (each stage within its hour limit).
115func haccp_cool_admit(stage1_hours: i64, stage2_hours: i64) -> i64 {
116 if stage1_hours > HC_COOL_S1_HOURS { return HC_HALT }
117 if stage2_hours > HC_COOL_S2_HOURS { return HC_HALT }
118 return HC_OK
119}
120
121func haccp_hot_hold_admit(observed_c: i64) -> i64 {
122 if observed_c >= HC_HOT_HOLD_C { return HC_OK }
123 return HC_HALT
124}
125
126func haccp_cold_hold_admit(observed_c: i64) -> i64 {
127 if observed_c <= HC_COLD_HOLD_C { return HC_OK }
128 return HC_HALT
129}
130
131func haccp_reheat_admit(observed_c: i64) -> i64 {
132 if observed_c >= HC_REHEAT_C { return HC_OK }
133 return HC_HALT
134}
135
136// CAN CCP: reuse the preservation 12-D botulinum cook (one source of truth).
137func haccp_can_admit(hold_mmin: i64, d_mmin: i64) -> i64 {
138 if pv_is_12d(hold_mmin, d_mmin) == 1 { return HC_OK }
139 return HC_HALT
140}
141
142// ACIDIFY CCP: pH must reach the botulinum line.
143func haccp_acidify_admit(ph_milli: i64) -> i64 {
144 if ph_milli <= HC_ACIDIFY_PH { return HC_OK }
145 return HC_HALT
146}