code wiki / _hdl_build / nx_wflow_approve_gate.nx
nx_wflow_approve_gate.nx source
↩ module page · 116 lines · 5925 B
1// nx_wflow_approve_gate.nx -- R4 HUMAN-IN-THE-LOOP approvals gate. An `approve` step PARKS the run
2// (status=WAIT, in-flight -- neither DONE nor FAILED) until an operator decision record lands on the
3// SAME event-sourced ledger (WFDEC ... decision=APPROVED|DENIED by=<who>); wf_resume then completes or
4// fail-stops it. DENY WINS (fail-safe). Proves: parked run gates later steps; resume WITHOUT a decision
5// never fabricates approval; APPROVED unlocks exactly once (no re-exec of OK steps); DENIED fail-stops;
6// bad decision word refused; a decision for the WRONG step does NOT unlock. ZERO forks.
7// license_tier: ORIGINAL expect_exit: 0
8import "nx_wflow_engine.nx"
9
10func ag_n(led: *u8, ridtok: *u8, st: *u8) -> i64 {
11 let szp: *i64 = sys_mmap(8) as *i64
12 let b: *u8 = wf_readall(led, szp)
13 if (b as i64) == 0 { return 0 - 1 }
14 return wf_lines_with2(b, szp[0], ridtok, st)
15}
16
17func main() -> i64 {
18 p("=== NX-WFLOW-APPROVE GATE (R4: approve step parks the run until an operator decision; deny wins) ===\n" as *u8)
19 var ok: i64 = 1
20 let now: i64 = wf_now()
21 let led: *u8 = sys_mmap(128)
22 var lo: i64 = 0
23 lo = wf_cat(led, lo, "/tmp/wfappr_" as *u8)
24 lo = wf_catn(led, lo, now)
25 lo = wf_cat(led, lo, ".log" as *u8)
26 led[lo] = 0 as u8
27
28 let flows: *i64 = sys_mmap(8 * 4) as *i64
29 flows[0] = "fa1|expense|-" as *u8 as i64
30 let steps: *i64 = sys_mmap(8 * 8) as *i64
31 steps[0] = "fa1|1|create-task|-|File the venue request|1" as *u8 as i64
32 steps[1] = "fa1|2|approve|operator|Spend 500 on the pavilion hire|1" as *u8 as i64
33 steps[2] = "fa1|3|notify|-|Approved and booked|1" as *u8 as i64
34
35 let cx: *i64 = sys_mmap(32) as *i64
36 cx[0] = steps as i64
37 cx[1] = 3
38 cx[2] = led as i64
39 cx[3] = 0
40
41 // T0 load validation: approve needs an approver name
42 let l0: i64 = wf_load(steps, 3)
43 let bad: *i64 = sys_mmap(16) as *i64
44 bad[0] = "f9|1|approve|-|x|1" as *u8 as i64
45 let lb: i64 = wf_load(bad, 1)
46 p(" T0 load=" as *u8); pn(l0); p(" badNoApprover=" as *u8); pn(lb); p("\n" as *u8)
47 if l0 != 3 { ok = 0 }
48 if lb != (0 - 1) { ok = 0 }
49
50 // T1 fire -> step1 OK, step2 PARKS (WAIT), step3 gated, run in-flight (no DONE, no FAILED)
51 let r1: i64 = wf_fire(cx, flows, 1, "expense~amount=500~id=A1" as *u8)
52 let w1: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=WAIT" as *u8)
53 let d1: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=DONE" as *u8)
54 let f1: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=FAILED" as *u8)
55 let s3a: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=3 " as *u8)
56 p(" T1 parked started=" as *u8); pn(r1); p(" wait=" as *u8); pn(w1); p(" done=" as *u8); pn(d1); p(" failed=" as *u8); pn(f1); p(" step3lines=" as *u8); pn(s3a); p("\n" as *u8)
57 if r1 != 1 { ok = 0 }
58 if w1 != 1 { ok = 0 }
59 if d1 != 0 { ok = 0 }
60 if f1 != 0 { ok = 0 }
61 if s3a != 0 { ok = 0 }
62
63 // T2 resume WITHOUT a decision -> still parked, approval is never fabricated
64 let rs1: i64 = wf_resume(cx)
65 let w2: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=WAIT" as *u8)
66 let d2: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=DONE" as *u8)
67 let s3b: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=3 " as *u8)
68 p(" T2 nofabricate resumed=" as *u8); pn(rs1); p(" wait=" as *u8); pn(w2); p(" done=" as *u8); pn(d2); p(" step3lines=" as *u8); pn(s3b); p("\n" as *u8)
69 if rs1 != 1 { ok = 0 }
70 if w2 != 2 { ok = 0 }
71 if d2 != 0 { ok = 0 }
72 if s3b != 0 { ok = 0 }
73
74 // T3 APPROVED -> resume completes step2 + step3, DONE; step1 never re-executed
75 let dc: i64 = wf_decide(cx, "A1.fa1" as *u8, 2, "APPROVED" as *u8, "operator" as *u8)
76 let rs2: i64 = wf_resume(cx)
77 let d3: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=DONE" as *u8)
78 let s1once: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=1 status=OK" as *u8)
79 let s3c: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=3 status=OK" as *u8)
80 p(" T3 approved deciderc=" as *u8); pn(dc); p(" resumed=" as *u8); pn(rs2); p(" done=" as *u8); pn(d3); p(" step1okOnce=" as *u8); pn(s1once); p(" step3ok=" as *u8); pn(s3c); p("\n" as *u8)
81 if dc != 0 { ok = 0 }
82 if rs2 != 1 { ok = 0 }
83 if d3 != 1 { ok = 0 }
84 if s1once != 1 { ok = 0 }
85 if s3c != 1 { ok = 0 }
86
87 // T4 DENIED path -> run fail-stops at the approval, step3 never runs
88 wf_fire(cx, flows, 1, "expense~amount=9000~id=A2" as *u8)
89 wf_decide(cx, "A2.fa1" as *u8, 2, "DENIED" as *u8, "operator" as *u8)
90 let rs3: i64 = wf_resume(cx)
91 let f4: i64 = ag_n(led, " rid=A2.fa1 " as *u8, "status=FAILED" as *u8)
92 let d4: i64 = ag_n(led, " rid=A2.fa1 " as *u8, "status=DONE" as *u8)
93 let s3d: i64 = ag_n(led, " rid=A2.fa1 " as *u8, " step=3 status=OK" as *u8)
94 p(" T4 denied resumed=" as *u8); pn(rs3); p(" failed=" as *u8); pn(f4); p(" done=" as *u8); pn(d4); p(" step3ok=" as *u8); pn(s3d); p("\n" as *u8)
95 if rs3 != 1 { ok = 0 }
96 if f4 != 1 { ok = 0 }
97 if d4 != 0 { ok = 0 }
98 if s3d != 0 { ok = 0 }
99
100 // T5 negctl precision: bad decision word refused; a decision for the WRONG step does not unlock
101 let db: i64 = wf_decide(cx, "A3.fa1" as *u8, 2, "MAYBE" as *u8, "operator" as *u8)
102 wf_fire(cx, flows, 1, "expense~amount=42~id=A3" as *u8)
103 wf_decide(cx, "A3.fa1" as *u8, 1, "APPROVED" as *u8, "operator" as *u8)
104 let rs4: i64 = wf_resume(cx)
105 let d5: i64 = ag_n(led, " rid=A3.fa1 " as *u8, "status=DONE" as *u8)
106 let w5: i64 = ag_n(led, " rid=A3.fa1 " as *u8, "status=WAIT" as *u8)
107 p(" T5 negctl badword=" as *u8); pn(db); p(" resumed=" as *u8); pn(rs4); p(" done=" as *u8); pn(d5); p(" waits=" as *u8); pn(w5); p("\n" as *u8)
108 if db != (0 - 1) { ok = 0 }
109 if d5 != 0 { ok = 0 }
110 if w5 < 2 { ok = 0 }
111
112 p("NX-WFLOW-APPROVE-GATE ledger=" as *u8); p(led); p(" forks=0 " as *u8)
113 if ok == 1 { p("verdict=GREEN\n" as *u8); return 0 }
114 p("verdict=RED\n" as *u8)
115 return 1
116}