code wiki / (root) / nx_wflow_approve_capacity_gate_t278.nx

nx_wflow_approve_capacity_gate_t278.nx source

↩ module page · 123 lines · 6429 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_capacity_candidate_t278.nx" 9import "nx_gate_verdict.nx" 10 11func ag_n(led: *u8, ridtok: *u8, st: *u8) -> i64 { 12 let szp: *i64 = sys_mmap(8) as *i64 13 let b: *u8 = wf_readall(led, szp) 14 if (b as i64) == 0 { return 0 - 1 } 15 return wf_lines_with2(b, szp[0], ridtok, st) 16} 17 18func main() -> i64 { 19 p("=== NX-WFLOW-APPROVE GATE (R4: approve step parks the run until an operator decision; deny wins) ===\n" as *u8) 20 var ok: i64 = 1 21 let now: i64 = wf_now() 22 let led: *u8 = sys_mmap(128) 23 var lo: i64 = 0 24 lo = wf_cat(led, lo, "/tmp/wfappr_" as *u8) 25 lo = wf_catn(led, lo, now) 26 lo = wf_cat(led, lo, ".log" as *u8) 27 led[lo] = 0 as u8 28 29 let flows: *i64 = sys_mmap(8 * 4) as *i64 30 flows[0] = "fa1|expense|-" as *u8 as i64 31 let steps: *i64 = sys_mmap(8 * 8) as *i64 32 steps[0] = "fa1|1|create-task|-|File the venue request|1" as *u8 as i64 33 steps[1] = "fa1|2|approve|operator|Spend 500 on the pavilion hire|1" as *u8 as i64 34 steps[2] = "fa1|3|notify|-|Approved and booked|1" as *u8 as i64 35 36 let cx: *i64 = sys_mmap(32) as *i64 37 cx[0] = steps as i64 38 cx[1] = 3 39 cx[2] = led as i64 40 cx[3] = 0 41 42 // T0 load validation: approve needs an approver name 43 let l0: i64 = wf_load(steps, 3) 44 let bad: *i64 = sys_mmap(16) as *i64 45 bad[0] = "f9|1|approve|-|x|1" as *u8 as i64 46 let lb: i64 = wf_load(bad, 1) 47 p(" T0 load=" as *u8); pn(l0); p(" badNoApprover=" as *u8); pn(lb); p("\n" as *u8) 48 if l0 != 3 { ok = 0 } 49 if lb != (0 - 1) { ok = 0 } 50 51 // T1 fire -> step1 OK, step2 PARKS (WAIT), step3 gated, run in-flight (no DONE, no FAILED) 52 let r1: i64 = wf_fire(cx, flows, 1, "expense~amount=500~id=A1" as *u8) 53 let w1: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=WAIT" as *u8) 54 let d1: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=DONE" as *u8) 55 let f1: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=FAILED" as *u8) 56 let s3a: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=3 " as *u8) 57 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) 58 if r1 != 1 { ok = 0 } 59 if w1 != 1 { ok = 0 } 60 if d1 != 0 { ok = 0 } 61 if f1 != 0 { ok = 0 } 62 if s3a != 0 { ok = 0 } 63 64 // T2 resume WITHOUT a decision -> still parked, approval is never fabricated 65 let rs1: i64 = wf_resume(cx) 66 let w2: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=WAIT" as *u8) 67 let d2: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=DONE" as *u8) 68 let s3b: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=3 " as *u8) 69 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) 70 if rs1 != 1 { ok = 0 } 71 if w2 != 2 { ok = 0 } 72 if d2 != 0 { ok = 0 } 73 if s3b != 0 { ok = 0 } 74 75 // T3 APPROVED -> resume completes step2 + step3, DONE; step1 never re-executed 76 let dc: i64 = wf_decide(cx, "A1.fa1" as *u8, 2, "APPROVED" as *u8, "operator" as *u8) 77 let rs2: i64 = wf_resume(cx) 78 let d3: i64 = ag_n(led, " rid=A1.fa1 " as *u8, "status=DONE" as *u8) 79 let s1once: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=1 status=OK" as *u8) 80 let s3c: i64 = ag_n(led, " rid=A1.fa1 " as *u8, " step=3 status=OK" as *u8) 81 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) 82 if dc != 0 { ok = 0 } 83 if rs2 != 1 { ok = 0 } 84 if d3 != 1 { ok = 0 } 85 if s1once != 1 { ok = 0 } 86 if s3c != 1 { ok = 0 } 87 88 // T4 DENIED path -> run fail-stops at the approval, step3 never runs 89 wf_fire(cx, flows, 1, "expense~amount=9000~id=A2" as *u8) 90 wf_decide(cx, "A2.fa1" as *u8, 2, "DENIED" as *u8, "operator" as *u8) 91 let rs3: i64 = wf_resume(cx) 92 let f4: i64 = ag_n(led, " rid=A2.fa1 " as *u8, "status=FAILED" as *u8) 93 let d4: i64 = ag_n(led, " rid=A2.fa1 " as *u8, "status=DONE" as *u8) 94 let s3d: i64 = ag_n(led, " rid=A2.fa1 " as *u8, " step=3 status=OK" as *u8) 95 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) 96 if rs3 != 1 { ok = 0 } 97 if f4 != 1 { ok = 0 } 98 if d4 != 0 { ok = 0 } 99 if s3d != 0 { ok = 0 } 100 101 // T5 negctl precision: bad decision word refused; a decision for the WRONG step does not unlock 102 let db: i64 = wf_decide(cx, "A3.fa1" as *u8, 2, "MAYBE" as *u8, "operator" as *u8) 103 wf_fire(cx, flows, 1, "expense~amount=42~id=A3" as *u8) 104 wf_decide(cx, "A3.fa1" as *u8, 1, "APPROVED" as *u8, "operator" as *u8) 105 let rs4: i64 = wf_resume(cx) 106 let d5: i64 = ag_n(led, " rid=A3.fa1 " as *u8, "status=DONE" as *u8) 107 let w5: i64 = ag_n(led, " rid=A3.fa1 " as *u8, "status=WAIT" as *u8) 108 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) 109 if db != (0 - 1) { ok = 0 } 110 if d5 != 0 { ok = 0 } 111 if w5 < 2 { ok = 0 } 112 113 p("NX-WFLOW-APPROVE-GATE ledger=" as *u8); p(led); p(" forks=0 " as *u8) 114 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 115 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 116 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 117 let ctr__dry: *i64 = gv_ctr() 118 ctr__dry[0] = ok 119 ctr__dry[1] = 1 120 let rc__dry: i64 = gv_verdict("WFLOW-APPROVE-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 121 sys_exit(rc__dry) 122 return rc__dry 123}