== A. every CAS template through desktop Giac 1.9, against Python fractions ==
   (icas: // (c) 2001, 2021 B. Parisse & others)
PASS A1 bt_Rat: decimals as n/10^d, count ratios recovered exactly (92/95, 7/9, 2/3)   []
PASS A1 bt_Rat: a value with more than 6 decimals is rounded and flagged (kind 2)   [123457, 1000000, 2]
PASS A2 1188 CAS texts (PPV, NPV, 1 - PPV, break-even; 1 and 2 tests): Giac = Python fractions   []
PASS A2 no CAS text holds a decimal point (BRIEF_CAS rule 1)   []
PASS A3 ppv(prior 0.01, 0.95 / 0.95) = 19/118   19/118
PASS A3 npv(prior 0.01, 0.95 / 0.95) = 1881/1882   1881/1882
PASS A3 be(prior 0.01, 0.95 / 0.95) = 1/20   1/20
PASS A3 ppv(prior 0.1, 0.9 / 0.95) = 2/3   2/3
PASS A3 ppv(prior 0.1, 0.9 / 0.95, 2 tests) = 36/37   36/37
PASS A3 be(prior 0.1, 0.9 / 0.95, 2 tests) = 1/325   1/325
PASS A3 ppv(prior 0.05, 0.9 / 0.968421) = 3/5   3/5
PASS A3 comp(prior 0.1, 0.9 / 0.95) = 1/3   1/3
PASS A3 natural frequencies at N = 10000: TP 95, FP 495, PPV 95/590 = 19/118 = 0.161
PASS A4 8a: the solves arrive in the Prime's braces ({(-btc+1)/(-btc+bts+1)}, {1/(btl*btm+1)}), which bt_Strip drops
PASS A4 symbolic break-even, 1 test: Giac (-btc+1)/(-btc+bts+1) = SymPy (1 - btc)/(-btc + bts + 1)
PASS A4 symbolic break-even, 2 tests: Giac 1/(btl*btm+1) = SymPy 1/(btl*btm + 1)
PASS A4 the solve is linear in the prior: exactly one root, so the Strip step needs no choice
PASS A5 bt_BEp (PPL, draws the mark) = the CAS break-even, every test and both chains   max diff 6.9e-17

== B. the program in the harness (ppl_run_v3d), cas = giac and cas = fail ==
PASS B1 defaults: panel PPV = 2/3, NPV = 171/173, badge exact (CAS)   ['PPV = 2/3', 'NPV = 171/173', 'exact (CAS), tap', 'Most T+ are false', 'if prior < 5.3 %']
PASS B1 defaults: break-even lines "Most T+ are false / if prior < 5.3 %" (1/19)   ['Most T+ are false', 'if prior < 5.3 %']
PASS B1 defaults: the CAS was asked 3 times for the first frame (PPV, NPV, break-even)   ['string((1/10*9/10)/(1/10*9/10+9/10*5/100))', 'string((9/10*95/100)/(9/10*95/100+1/10*1/10))', 'string(solve((btq*9/10)/(btq*9/10+(1-btq)*5/100)=1/2,btq))']
PASS B1 summary on exit carries the exact PPV   BayesTree: PPV = 0.667 = 2/3, NPV = 0.988 (prior 0.100, sens 0.900, spec 0.950)
PASS B1 8a: every CAS text asks for string(...), every answer arrives quoted; no CAS variable assigned, no CAS call of a variable   [('string((1/10*9/10)/(1/10*9/10+9/10*5/100))', '"2/3"', 96.42791748046875)]
PASS B1 break-even mark: magenta line above the track at x = 214 (bt_SlX(1/19)), red tint left of it   1 mark, 1 tint
PASS B1 layout: no tree text reaches the soft keys (y >= 218) or x 318   []
PASS B2 example tree panel: PPV = 19/118, NPV = 1881/1882   ['PPV = 19/118', 'NPV = 1881/1882', 'exact (CAS), tap', 'Most T+ are false', 'if prior < 5.0 %']
PASS B2 Exact page 1 shows "Picture N = 10000 people (natural frequencies):"
PASS B2 Exact page 1 shows "TP = 95, FP = 495, FN = 5, TN = 9405"
PASS B2 Exact page 1 shows "PPV = TP/(TP+FP) = 95/590"
PASS B2 Exact page 1 shows "= 19/118 = 0.161"
PASS B2 Exact page 1 shows "= 1881/1882 = 0.999"
PASS B2 Exact page 1 shows "prior = 1/20 = 0.0500 = 5.0 %"
PASS B2 Exact page 1 shows "Below a prior of 5.0 %, a positive result is more"
PASS B2 Exact page 2 shows "PPV = TP/(TP+FP) = p×se / (p×se + (1−p)×(1−sp))"
PASS B2 Exact page 2 shows "= 0.01×0.95 / (0.01×0.95 + 0.99×0.05)"
PASS B2 Exact page 2 shows "p = (-sp+1)/(-sp+se+1)"
PASS B2 Exact page 2 shows "your numbers: p = 1/20 = 0.0500"
PASS B2 both pages carry the badge "exact (CAS)"
PASS B2 layout: no Exact-page line past x 318 (estimated widths)   []
PASS B2 opening the page asked the CAS once more (the symbolic solve), the rest was cached   ['frac', 'frac', 'solve', 'solve']
PASS B3 slider drag: every drag frame says "exact: on lift" (no stale fraction shown)   ['exact: on lift', 'exact: on lift', 'exact: on lift', 'exact: on lift', 'exact: on lift']
PASS B3 slider drag: CAS asked 3 times at the start and 2 times after the lift (PPV, NPV) -- no solve   ['frac', 'frac', 'solve', 'frac', 'frac']
PASS B3 slider drag: the break-even lines stay put through the drag (they do not depend on the prior)   {'if prior < 5.3 %'}
PASS B3 8b: slider drag: the four leaves re-count in every frame, the lift frame too, each pair adding up (7 frames)   []
PASS B3 slider drag: exactly one extra frame after the lift (the CAS update), same prior as the last sample   7 frames
PASS B3 after the lift the panel shows the exact PPV for the new prior (0.001): PPV = 2/113   ['PPV = 2/113', 'NPV = 18981/18983', 'exact (CAS), tap', 'Most T+ are false', 'if prior < 5.3 %']
PASS B4 sens drag: the break-even mark moves in the drag frame, before the lift (PPL), [214.0, 215.0, 215.0]
PASS B4 sens drag: CAS asked 3 times at the start, then 3 after the lift (PPV, NPV, solve)   ['frac', 'frac', 'solve', 'frac', 'frac', 'solve']
PASS B5 a key (Right, spec 0.96): one update of 3 CAS calls, then the panel is exact   6 calls, ['PPV = 5/7', 'NPV = 432/437', 'exact (CAS), tap', 'Most T+ are false', 'if prior < 4.3 %']
PASS B5 back to a value seen before still asks again (one cache, by design): 9 calls   9
PASS B5 tapping a box and closing its page: no new CAS call (inputs unchanged)   3
PASS B6 forensic panel: PPV = 2/3, 1−PPV = 1/3, "Most matches false / below 1 in 19"   ['PPV = 2/3', '1−PPV = 1/3', 'exact (CAS), tap', 'Most matches false', 'below 1 in 19']
PASS B7 two tests panel: PPV = 36/37 (= 0.972973), NPV = 19/23, "Most T+T+ false / if prior < 0.31 %"   ['PPV = 36/37', 'NPV = 19/23', 'exact (CAS), tap', 'Most T+T+ false', 'if prior < 0.31 %']
PASS B7 two tests Exact page: N = 100000, 13500 positive on test 1, prior = 1/325
PASS B7 two tests page 2: the CAS solve in LR form, p = 1/(LR₁×LR₂+1)
PASS B7 two tests: the slider mark sits at the base prior 1/325 (x = 185)
PASS B8 Enter counts 45/5/30/920: PPV = 3/5 (= 45/75), NPV = 184/185 (= 920/925)   ['PPV = 3/5', 'NPV = 184/185', 'exact (CAS), tap', 'Most T+ are false', 'if prior < 3.4 %']
PASS B9 a tap on the exact panel opens the Exact page
PASS B10 soft key page 3, slot 5 "Exact" opens the Exact page
PASS B11 PPV view: magenta square at the crossing (x 53, y 107)
PASS B11 PPV view: "Below 5.3 %, most positives are false (PPV < 1/2)."
PASS B11 PPV view layout: tick labels at y 189, the sentence at y 199, readout 212, footer 226   [189.0, 199.0, 212.0, 226.0]
PASS B12 cas=fail: no runtime error, the program ends normally   None
PASS B12 cas=fail: tree panel shows the decimals and "decimal, tap"   ['PPV = 0.161', 'NPV = 0.999', 'decimal, tap', 'Most T+ are false', 'if prior < 5.0 %']
PASS B12 cas=fail: Exact pages say "decimal", keep the natural-frequency fraction 95/590 (PPL counts)
PASS B12 cas=fail: page 2 gives the break-even by hand and says no CAS answer
PASS B12 cas=fail: the summary has no fraction   BayesTree: PPV = 0.161, NPV = 0.999 (prior 0.010, sens 0.950, spec 0.950)
PASS B12 8c: beta 8b in harness v3d STOPS on the same cas=fail run (INSTRING: Invalid input (the first argument is an empty string -- emulator, 17 O)
PASS B13 slow CAS: after the first update the tree says "tap for exact" and asks no more   ['tap for exact', 'Most T+ are false', 'if prior < 4.3 %']
PASS B13 slow CAS: a tap still opens the Exact page, which asks the CAS and says the tree waits for a tap
PASS B14 stale-key mode: no page skipped on the way to and through the Exact pages   []
PASS B15 help: four pages, page 4 "exact answers (CAS)", all text above y 218 and inside x 318
PASS B17 answers wrapped in quotes: the kit strips them, the panel still shows PPV = 2/3, NPV = 171/173   ['PPV = 2/3', 'NPV = 171/173', 'exact (CAS), tap', 'Most T+ are false', 'if prior < 5.3 %']
PASS B17 a decimal answer (approximate mode) is not shown as exact: PPV = 0.667, badge decimal   ['PPV = 0.667', 'NPV = 171/173', 'decimal, tap', 'Most T+ are false', 'if prior < 5.3 %']

== B16. beta 7's verify_BayesTree.py run against beta 8e (harness v3e) ==
   47 PASS, 5 FAIL
   FAIL A summary returned   BayesTree: PPV = 0.667 = 2/3, NPV = 0.988 (prior 0.100, sens 0.900, spec 0.950)
   FAIL B slider: one redraw per changed drag sample   ['prior 0.097', 'prior 0.010', 'prior 0.014', 'prior 0.860', 'prior 0.500', 'prior 0.500']
   FAIL B slider: all six boxes re-count at every sample   priors [0.097, 0.01, 0.014, 0.86, 0.5]
   FAIL E More cycles three pages and back   ['Values', 'Counts', 'ROC', 'PPV', '2×2', 'More']
   FAIL I breadcrumb 0.10 -> 0.67 -> 0.97   ('0.10', None, None)
PASS B16 every other beta 7 check passes on beta 8e (47 pass; the 5 intended changes listed above)   []

== G. beta 8 in harness v3d (the CAS finding stays), and beta 8b against beta 8a ==
PASS G beta 8 in v3d: its bt_Cas hands back its own text, so the panel shows decimals only (['PPV = 0.667', 'NPV = 0.988', 'decimal, tap'])   ['PPV = 0.667', 'NPV = 0.988', 'decimal, tap', 'Most T+ are false', 'if prior < 5.3 %']

G. beta 8b against beta 8a
PASS beta 8b: routines that differ from beta 8a: ['BayesTree', 'bt_FaganView', 'bt_PrevView', 'bt_RCnt', 'bt_RPrb', 'bt_RocView', 'bt_WaitAny', 'bt_fmtq'] (want ['BayesTree', 'bt_FaganView', 'bt_PrevView', 'bt_RCnt', 'bt_RPrb', 'bt_RocView', 'bt_WaitAny', 'bt_fmtq'])
PASS beta 8b: bt_RCnt, bt_RPrb, bt_fmtq are new
PASS beta 8b: one new file-level LOCAL line, the stale-click list
PASS beta 8b: outside those routines and that line only comments differ
PASS beta 8b: bt_Cas (the CAS call form) is byte-identical to beta 8a
  routines that differ from beta 8a: BayesTree, bt_FaganView, bt_PrevView, bt_RCnt, bt_RPrb, bt_RocView, bt_WaitAny, bt_fmtq; everything else differs only in comments

== S. 8b: soft keys under WAIT(-1), as the emulator answers (harness v3d) ==
PASS S1 8b: soft key 1 (Values) runs from the type-3 strip click alone   ['Exact Values'] ['BayesTree']
PASS S1 8b: soft key 2 (Counts) runs from the type-3 strip click alone   ['Leaf Counts (2x2)'] ['BayesTree']
PASS S1 8b: soft key 3 (ROC) runs from the type-3 strip click alone   [] ['BayesTree', 'bt_RocView']
PASS S1 8b: soft key 4 (PPV) runs from the type-3 strip click alone   [] ['BayesTree', 'bt_PrevView']
PASS S1 8b: soft key 5 (2×2) runs from the type-3 strip click alone   [] ['BayesTree', 'bt_Tbl2View']
PASS S2 8b: beta 8a in v3d: soft key 1 does nothing (the emulator: "tapping Values or ROC does nothing")   ['BayesTree']
PASS S2 8b: beta 8a in v3d: soft key 2 does nothing (the emulator: "tapping Values or ROC does nothing")   ['BayesTree']
PASS S2 8b: beta 8a in v3d: soft key 3 does nothing (the emulator: "tapping Values or ROC does nothing")   ['BayesTree']
PASS S3 8b: More (slot 6) steps page 1 -> 2 -> 3 -> 1 from type-3 clicks   ['Values', 'Diag', 'Test 2', 'Values']
PASS S3 8b: page 2 slot 5 (Help) opens the help pages   ['BayesTree', 'bt_HelpView']
PASS S3 8b: page 3 slot 4 (Leaves) turns the leaves into shares (T+ 0.090 ...)   ['T+ 0.090', 'T− 0.010', 'T+ 0.045', 'T− 0.855']
PASS S4 8b: a type-0 PRESS on the strip (v3b model: {0, x, y}, {2}) does nothing -- neutralised   []
PASS S4 8b: a firmware that sent {0}, {2} AND {3} for a strip tap would still run the key ONCE   ['Leaf Counts (2x2)']
PASS S4 8b: a tap on the root box ({0}, {2}, {3}) opens its node page once, not twice   1 node pages
PASS S4 8b: a tap on a branch number opens its page once, on the lift ({2}), not again on the click   1 node pages
PASS S5 8b: bt_PrevView closed by a tap on its footer (x 150, y 226): its {2}, {3} reach the tree and run no soft key   ['BayesTree', 'bt_PrevView'] []
PASS S5 8b: bt_RocView closed by a tap on its footer (x 100, y 226): its {2}, {3} reach the tree and run no soft key   ['BayesTree', 'bt_RocView'] []
PASS S5 8b: bt_FaganView closed by a tap on its footer (x 230, y 226): its {2}, {3} reach the tree and run no soft key   ['BayesTree', 'bt_FaganView'] []
PASS S5 8b: without the guard the PPV view's closing click WOULD run soft key 3 (ROC) on the tree   ['BayesTree', 'bt_PrevView', 'bt_RocView']
PASS S5 8b: the guard skips that one click only: a tap at the same spot afterwards runs ROC   ['BayesTree', 'bt_PrevView', 'bt_RocView']
PASS S6 8b: mouse_tail (not measured): a node page closed by a footer tap leaves its click, which the tree skips   ['BayesTree', 'bt_NodeInfo']
PASS S6 8b: mouse_tail: four help pages closed by footer taps, back on the tree, no soft key fired   ['BayesTree', 'bt_HelpView']
PASS S7 8b: the stale-click list is filled by the three WAIT(-1) views and bt_WaitAny, and reset by keys and presses
PASS S7 8b: no WAIT(-1) type-0 test at y >= 218 acts any more; the strip is read from type 3 only

== L. 8b: every pair on the tree adds up ==
PASS L1 8b: sens 0.835, D+ 100: T+ 84, T− 16 (84 + 16 = 100); D− 900: T+ 45, T− 855   ['T+ 84', 'T− 16', 'T+ 45', 'T− 855']
PASS L1 8b: beta 8a shows the emulator's T+ 84, T− 17 (101) for the same tree   ['T+ 84', 'T− 17', 'T+ 45', 'T− 855']
PASS L2 8b: 144 trees (priors, sens/spec pairs, N 10, 37, 1000): root = D+ + D−, D+ = TP + FN, D− = FP + TN, and every branch pair adds to 1   []
PASS L2 8b: every count is within one person of its exact value   []
PASS L2 8b: 12 two-test trees (root = test 1's positives): every pair adds up   []
PASS L3 8b: 6 trees in shares: every pair adds up at three decimals   []
PASS L3 8b: a share that rounds to 0 still says "< 0.001", not "0.000" (prior 0.0001: TP, FN, D+, the prior branch)   ['prior < 0.001', '< 0.001', 'D+ < 0.001', 'T+ < 0.001', 'T− < 0.001', 'odds < 0.001 × LR+ = 0.002']
PASS L4 8b: the FN box's page says "count = 16  (0.016 of N)", as the box   ['False negatives', 'count = 16  (0.016 of N)']
PASS L4 8b: the 2×2 view: 84 + 16 = 100, 45 + 855 = 900, columns 129 + 871 = 1000 (8a: 84 + 17 ≠ 100)   [(84.0, 16.0, 100.0), (45.0, 855.0, 900.0), (129.0, 871.0, 1000.0)]

== C. 8b: the first-level numbers off the branch lines ==
PASS C1 8b: the prior number (0.100 at x 98, y 78) does not touch either branch line   (97.0, 78.0, 126.5, 88.0)
PASS C1 8b: the 1 - prior number (0.900 at x 98, y 136) does not touch either branch line   (97.0, 136.0, 126.5, 146.0)
PASS C1 8b: nothing is drawn at the old spots (x 84, y 70 / 142)
PASS C1 8b: (for the record) the old spot x 84, y 70 lay on the upper branch line
PASS C2 8b: a tap on the moved number (110, 82) opens "Branch: P(D+) = prior"   ['Branch: P(D+) = prior']
PASS C2 8b: a tap on the moved number (110, 140) opens "Branch: P(D−) = 1 − prior"   ['Branch: P(D−) = 1 − prior']
PASS C3 8b: no tree text runs past x 318   []

== G (8c). beta 8c against beta 8b (the two files, as before) ==
PASS G 8c: routines that differ from beta 8b: ['BayesTree', 'bt_Cas', 'bt_Crumb', 'bt_DiagView', 'bt_ExactView', 'bt_FaganView', 'bt_HelpView', 'bt_NumS', 'bt_Pct', 'bt_PrevView', 'bt_Rep', 'bt_RocView', 'bt_Strip', 'bt_fmtq']   want ['BayesTree', 'bt_Cas', 'bt_Crumb', 'bt_DiagView', 'bt_ExactView', 'bt_FaganView', 'bt_HelpView', 'bt_NumS', 'bt_Pct', 'bt_PrevView', 'bt_Rep', 'bt_RocView', 'bt_Strip', 'bt_fmtq']
PASS G 8c: bt_Crumb is the one new routine; bt_WaitAny (it still appends its tap) is byte-identical
PASS G 8c: outside those routines the one new line is the threshold, "LOCAL btQueuedMs := 100;"; everything else is comments
PASS G 8c: bt_Cas keeps its IFERR call line and its quote strip byte-identical (only the INSTRING guard is new)
PASS G 8c: the stale test reads the threshold by name, and the threshold is set in ONE place
PASS G 8c: the type-3 soft-key logic the emulator confirmed is unchanged (slot MIN(6, IP(x/53.4)+1) on a non-stale type 3 at y >= 218)

== R. 8c: the second emulator round ==
PASS R1 8c: beta 8b, the emulator's case: 2x2 closed on its footer at the ROC key, then ROC once -> NOTHING (the tap is eaten by the stale list)   ['BayesTree', 'bt_Tbl2View']
PASS R1 8c: beta 8b: ... a second ROC tap works (the emulator: "the first tap is ignored, the second works")   ['BayesTree', 'bt_RocView', 'bt_Tbl2View']
PASS R1 8c: beta 8c: the same taps -- ROC opens on the FIRST tap (a real, later tap at the spot counts)   ['BayesTree', 'bt_RocView', 'bt_Tbl2View']
PASS R1 8c: the same at the Values key's spot: the 2x2 view closes there, the next tap there runs Values   ['BayesTree', 'bt_Tbl2View'] ['Exact Values']
PASS R1 8c: the same at the PPV key's spot: the 2x2 view closes there, the next tap there runs PPV   ['BayesTree', 'bt_PrevView', 'bt_Tbl2View'] []
PASS R1 8c: the same at the More key's spot: the 2x2 view closes there, the next tap there runs More   ['BayesTree', 'bt_Tbl2View'] []
PASS R1 8c: a click ALREADY QUEUED at the ROC spot (mouse_tail, a clock that has not moved (plain v3d)) is still skipped: no ROC   ['BayesTree', 'bt_Tbl2View']
PASS R1 8c: a click ALREADY QUEUED at the ROC spot (mouse_tail, the student's taps 400 ms apart) is still skipped: no ROC   ['BayesTree', 'bt_Tbl2View']
PASS R1 8c: ... and the real ROC tap after that queued click still counts: ROC opens once   ['BayesTree', 'bt_RocView', 'bt_Tbl2View']
PASS R1 8c: (with the stale test patched out, that queued click WOULD run ROC -- so the skip is real)   ['BayesTree', 'bt_RocView', 'bt_Tbl2View']
PASS R1 8c: PPV closed by a tap outside its plot (150, 205): its queued {2}, {3} run nothing on the tree   ['BayesTree', 'bt_PrevView']
PASS R1 8c: with time passing, a tap on the root box still opens its page once   1
PASS R1 8c: mouse_tail with time passing: a node page closed on its footer leaves its click, which is skipped   ['BayesTree', 'bt_NodeInfo']
PASS R2 8c: bt_PrevView closes on a footer tap that arrives only as {3, 150, 226}; back on the tree, nothing else opens   ['BayesTree', 'bt_PrevView'] left over 1
PASS R2 8c: (beta 8b in the same model: the footer tap does nothing, Esc has to close bt_PrevView -- round 2's finding)   left over 0
PASS R2 8c: bt_RocView closes on a footer tap that arrives only as {3, 100, 226}; back on the tree, nothing else opens   ['BayesTree', 'bt_RocView'] left over 1
PASS R2 8c: (beta 8b in the same model: the footer tap does nothing, Esc has to close bt_RocView -- round 2's finding)   left over 0
PASS R2 8c: bt_FaganView closes on a footer tap that arrives only as {3, 230, 226}; back on the tree, nothing else opens   ['BayesTree', 'bt_FaganView'] left over 1
PASS R2 8c: (beta 8b in the same model: the footer tap does nothing, Esc has to close bt_FaganView -- round 2's finding)   left over 0
PASS R2 8c: ROC closed by a footer tap at the PPV key's spot, then PPV tapped: PPV opens (no stale entry)   ['BayesTree', 'bt_RocView', 'BayesTree', 'bt_PrevView', 'BayesTree']
PASS R2 8c: bt_PrevView: a type-0 tap outside the plot still means back (kept as it was)   ['BayesTree', 'bt_PrevView']
PASS R2 8c: bt_RocView: a type-0 tap outside the plot still means back (kept as it was)   ['BayesTree', 'bt_RocView']
PASS R2 8c: bt_FaganView: a type-0 tap outside the plot still means back (kept as it was)   ['BayesTree', 'bt_FaganView']
PASS R2 8c: a drag inside the PPV plot still moves the prior (several frames) and does not close the view   4 frames
PASS R3 8c: the emulator's tree (prior 0.041, 0.835 / 0.95 twice), 6.5 px per character: .04 → .42 → .92, no overlap, ends at x 85.5 <= 102   [('.04', 2.0), ('→', 24.5), ('.42', 34.0), ('→', 56.5), ('.92', 66.0)]
PASS R3 8c: the same tree in v3d's own 5.5 px model keeps the leading zeros: 0.04 → 0.42 → 0.92 (ends at x 91.0)   [('0.04', 2.0), ('→', 27.0), ('0.42', 35.5), ('→', 60.5), ('0.92', 69.0)]
PASS R3 8c: the same tree with an 11-px arrow (stress): .04 → .42 → .92, no overlap, ends at x 94.5   [('.04', 2.0), ('→', 24.5), ('.42', 38.5), ('→', 61.0), ('.92', 75.0)]
PASS R3 8c: (beta 8b, fixed at x 2 / 31 / 38 / 67 / 74: with an 11-px arrow each arrow runs into the next number, the emulator's "0.04 -0.42 -0.92")   [('→', '0.42'), ('→', '0.92')]
PASS R3 8c: 72 two-test trees (12 priors x 6 test pairs, the extremes included), 6.5 px per character: every crumb is 5 pieces, none overlaps the one before, all end left of x 102   []
     forms used (6.5 px model; 0 = bt_fmtB as is, 1 = no leading zero, 2 = two decimals): {2: 27, 1: 45}
     with an 11-px arrow (stress): 42 of 72 still fit; those that do not: [((0.0001, (0.835, 0.95), (0.835, 0.95)), '<.01 → <.01 → .03', 103.5), ((0.0001, (0.9, 0.95), (0.9, 0.95)), '<.01 → <.01 → .03', 103.5), ((0.0001, (0.999, 0.999), (0.999, 0.999)), '<.01 → .09 → >.99', 103.5), ((0.0001, (0.5, 0.5), (0.6, 0.7)), '<.01 → <.01 → <.01', 110.0)]
PASS R3 8c: with an 11-px arrow nothing ever overlaps either (the layout follows the measured widths)
PASS R3 8c: if TEXTOUT_P handed back no position past its start, the crumb is laid out at 7 px a character (10 for the arrow): .04 → .42 → .92, ends at x 97.0   [('.04', 2.0), ('→', 26.0), ('.42', 39.0), ('→', 63.0), ('.92', 76.0)]
PASS R4 8c: two tests, Exact page 1: "then test 2: TP = 28586225, FP = 2397500," and "  FN = 5648775, TN = 45552500" on two lines   ['Exact answers', 'exact (CAS)', 'Picture N = 1000000000: 82185000 test + on test 1,', 'then test 2: TP = 28586225, FP = 2397500,']
PASS R4 8c: two tests: every Exact-page line inside x 318, at most 15 lines, the last at y <= 206 (above the footer at 226)   13 lines, last y 180.0, wide []
PASS R4 8c: two tests, forensic: every Exact-page line inside x 318, at most 15 lines, the last at y <= 206 (above the footer at 226)   15 lines, last y 206.0, wide []
PASS R4 8c: (beta 8b: that line was 69 characters, to x 388 -- off the 320-px screen)
PASS R5 8c: Diagnostics says "For p-values / CIs on real data: EpiStats 2×2." (inside x 318), and ConTable2 is named nowhere in the program's text   ['All are cell-proportion functions: valid for', 'expected (forward) AND observed (counts) tables.', 'For p-values / CIs on real data: EpiStats 2×2.', 'any key or tap = back']
PASS R6 8c: soft-key page 3 reads ['Test 2', 'Reset', 'Forens', 'Leaves', 'Exact', 'More']; every label of the three pages has at most 6 characters   ['Leaves', '3 prev', 'Test 2']
PASS R7 8c: bt_Rep: a pattern at the end, at the start, the whole text, an empty pattern, an empty text, twice, none -- all right, no error   []
PASS R7 8c: (beta 8b's bt_Rep STOPS on a pattern at the end and on an empty text: ['INSTRING: Invalid input (the first argum', 'INSTRING: Invalid input (the first argum'])
PASS R7 8c: bt_Strip("") = "", "{1/20}" -> 1/20, "list[1/20]" -> 1/20, "[ ]" -> "" (no error)   ['', '1/20', '1/20', '']
PASS R7 8c: bt_Cas with the CAS failing returns "" without an error
PASS R7 8c: bt_Cas still answers (1/3 -> "1/3") and refuses an infinity (1/0 -> "")   [('string(1/3)', '"1/3"', 104.88152503967285), ('string(1/0)', '"±∞"', 95.67022323608398)]
PASS R7 8c: cas=fail end to end (one test): the tree badge "decimal, tap", both Exact pages "decimal", no error   ['PPV = 0.161', 'NPV = 0.999', 'decimal, tap'] 2
PASS R7 8c: (beta 8b on the same run STOPS: INSTRING: Invalid input (the first argument is an empty stri)
PASS R7 8c: cas=fail end to end (two tests): the tree badge "decimal, tap", both Exact pages "decimal", no error   ['PPV = 0.973', 'NPV = 0.826', 'decimal, tap'] 2
PASS R7 8c: (beta 8b on the same run STOPS: INSTRING: Invalid input (the first argument is an empty stri)
PASS R7 8c: cas=fail end to end (forensic): the tree badge "decimal, tap", both Exact pages "decimal", no error   ['PPV = 0.667', '1−PPV = 0.333', 'decimal, tap'] 2
PASS R7 8c: (beta 8b on the same run STOPS: INSTRING: Invalid input (the first argument is an empty stri)
PASS R7 8c: cas=fail end to end (forensic, two tests, soft key Exact): the tree badge "decimal, tap", both Exact pages "decimal", no error   ['PPV = 0.971', '1−PPV = 0.029', 'decimal, tap'] 2
PASS R7 8c: (beta 8b on the same run STOPS: INSTRING: Invalid input (the first argument is an empty stri)

== T (8d). no "=" before a relation (harness v3e) -- 8e: kept on the beta 8d and 8c files ==
     diagnostic view, prior 1 in 10,000, sens = spec = 0.01
       beta 8c: ['Cohen κ = > -0.001', 'F1 = < 0.001', 'diagnostic OR = < 0.001   (LR+/LR−)', 'prevalence = < 0.001']
       beta 8d: ['Cohen κ > -0.001', 'F1 < 0.001', 'diagnostic OR < 0.001   (LR+/LR−)', 'prevalence < 0.001']
PASS T1 8d: diagnostic view, prior 1 in 10,000, sens = spec = 0.01 -- prevalence < 0.001; F1 < 0.001; diagnostic OR < 0.001; Cohen κ > -0.001   ['Cohen κ > -0.001', 'F1 < 0.001', 'diagnostic OR < 0.001   (LR+/LR−)', 'prevalence < 0.001']
     diagnostic view, sens 0.5, spec 0.5004 (Youden J, kappa, MCC just above 0)
       beta 8c: ['Cohen κ = < 0.001', 'MCC = < 0.001', 'Youden J = < 0.001   (se+sp−1; ROC height above chance)']
       beta 8d: ['Cohen κ < 0.001', 'MCC < 0.001', 'Youden J < 0.001   (se+sp−1; ROC height above chance)']
PASS T1 8d: diagnostic view, sens 0.5, spec 0.5004 (Youden J, kappa, MCC just above 0) -- Youden J < 0.001; Cohen κ < 0.001; MCC < 0.001   ['Cohen κ < 0.001', 'MCC < 0.001', 'Youden J < 0.001   (se+sp−1; ROC height above chance)']
     diagnostic view, sens 0.5, spec 0.4996 (Youden J just below 0)
       beta 8c: ['Cohen κ = > -0.001', 'MCC = > -0.001', 'Youden J = > -0.001   (se+sp−1; ROC height above chance)']
       beta 8d: ['Cohen κ > -0.001', 'MCC > -0.001', 'Youden J > -0.001   (se+sp−1; ROC height above chance)']
PASS T1 8d: diagnostic view, sens 0.5, spec 0.4996 (Youden J just below 0) -- Youden J > -0.001   ['Cohen κ > -0.001', 'MCC > -0.001', 'Youden J > -0.001   (se+sp−1; ROC height above chance)']
     2 x 2 view
       beta 8c: ['cols:  PPV = < 0.001,  NPV = 0.990']
       beta 8d: ['cols:  PPV < 0.001,  NPV = 0.990']
PASS T1 8d: 2 x 2 view -- cols:  PPV < 0.001   ['cols:  PPV < 0.001,  NPV = 0.990']
     2 x 2 view, forensic, prior 0.999
       beta 8c: ['cols:  P(not source | match) = 1 − PPV = < 0.001']
       beta 8d: ['cols:  P(not source | match) = 1 − PPV < 0.001']
PASS T1 8d: 2 x 2 view, forensic, prior 0.999 -- = 1 − PPV < 0.001   ['cols:  P(not source | match) = 1 − PPV < 0.001']
     prevalence view
       beta 8c: ['PPV = < 0.001']
       beta 8d: ['PPV < 0.001']
PASS T1 8d: prevalence view -- PPV < 0.001   ['PPV < 0.001']
     prevalence view, prior 0.0001 (the readout at the cursor)
       beta 8c: ['PPV = < 0.001', 'prior = < 0.001:']
       beta 8d: ['PPV < 0.001', 'prior < 0.001:']
PASS T1 8d: prevalence view, prior 0.0001 (the readout at the cursor) -- prior < 0.001:   ['PPV < 0.001', 'prior < 0.001:']
     Fagan view, LR+ 0.0101
       beta 8c: ['LR+ = 1.000:  < 0.001 → PPV = < 0.001', 'LR− = 1.000:  < 0.001 → 1 − NPV = < 0.001']
       beta 8d: ['LR+ = 1.000:  < 0.001 → PPV < 0.001', 'LR− = 1.000:  < 0.001 → 1 − NPV < 0.001']
PASS T1 8d: Fagan view, LR+ 0.0101 -- PPV < 0.001   ['LR+ = 1.000:  < 0.001 → PPV < 0.001', 'LR− = 1.000:  < 0.001 → 1 − NPV < 0.001']
     three prevalences
       beta 8c: ['PPV = < 0.001']
       beta 8d: ['PPV < 0.001']
PASS T1 8d: three prevalences -- PPV < 0.001   ['PPV < 0.001']
     tree panel, prior 0.0001, sens = spec = 0.01
       beta 8c: ['< 0.001', 'PPV = < 0.001', 'odds < 0.001 × LR+ = < 0.001', 'prior < 0.001']
       beta 8d: ['< 0.001', 'PPV < 0.001', 'odds < 0.001 × LR+ < 0.001', 'prior < 0.001']
PASS T1 8d: tree panel, prior 0.0001, sens = spec = 0.01 -- PPV < 0.001   ['< 0.001', 'PPV < 0.001', 'odds < 0.001 × LR+ < 0.001', 'prior < 0.001']
     tree panel, forensic, prior 0.999
       beta 8c: ['1−PPV = < 0.001']
       beta 8d: ['1−PPV < 0.001']
PASS T1 8d: tree panel, forensic, prior 0.999 -- 1−PPV < 0.001   ['1−PPV < 0.001']
     tree panel, prior 0.999, sens = spec = 0.01
       beta 8c: ['NPV = < 0.001']
       beta 8d: ['NPV < 0.001']
PASS T1 8d: tree panel, prior 0.999, sens = spec = 0.01 -- NPV < 0.001   ['NPV < 0.001']
     Exact page, the CAS fails
       beta 8c: ['    = < 0.001  (decimal)']
       beta 8d: ['    < 0.001  (decimal)']
PASS T1 8d: Exact page, the CAS fails --     < 0.001  (decimal)   ['    < 0.001  (decimal)']
     Exact page, the CAS answers
       beta 8c: ['    = 1/989902 = < 0.001']
       beta 8d: ['    = 1/989902 < 0.001']
PASS T1 8d: Exact page, the CAS answers --     = 1/989902 < 0.001   ['    = 1/989902 < 0.001']
     Exact page, two near-perfect tests
       beta 8c: ['    prior = 1/998002 = < 0.0001 = 0.000 %', '    your numbers: p = 1/998002 = < 0.0001']
       beta 8d: ['    prior = 1/998002 < 0.0001 = 0.000 %', '    your numbers: p = 1/998002 < 0.0001']
PASS T1 8d: Exact page, two near-perfect tests --     prior = 1/998002 < 0.0001 = 0.000 %   ['    prior = 1/998002 < 0.0001 = 0.000 %', '    your numbers: p = 1/998002 < 0.0001']
     summary, prior 0.0001:
       beta 8c: BayesTree: PPV = < 0.001, NPV = 0.990 (prior < 0.001, sens 0.010, spec 0.010)
       beta 8d: BayesTree: PPV < 0.001, NPV = 0.990 (prior < 0.001, sens 0.010, spec 0.010)
PASS T1 8d: the summary, prior 0.0001: BayesTree: PPV < 0.001, NPV = 0.990 (prior < 0.001, sens 0.010, spec 0.010)
     summary, forensic, prior 0.999:
       beta 8c: BayesTree: PPV = 1.000, NPV = 0.500 (prior 0.999, sens 0.999, spec 0.999)
       beta 8d: BayesTree: PPV = 1.000, NPV = 0.500 (prior 0.999, sens 0.999, spec 0.999)
PASS T1 8d: the summary, forensic, prior 0.999: BayesTree: PPV = 1.000, NPV = 0.500 (prior 0.999, sens 0.999, spec 0.999)
     node pages, tiny prior: ['PPV = TP / (TP + FP) < 0.001', 'is the PPV < 0.001.', 'prior < 0.001', 'prior < 0.001 (1 in 10000)']
PASS T1 8d: node pages, tiny prior: beta 8c's text written without "= <" (26 pages)   []
     node pages, PPV near 1: ['1 − PPV < 0.001 is P(not source | match),', '1 − PPV < 0.001 is the probability the', 'which is 1 − PPV < 0.001.']
PASS T1 8d: node pages, PPV near 1: beta 8c's text written without "= <" (26 pages)   []
     node pages, NPV near 0: ['Correctly excluded.  NPV < 0.001.', 'NPV = TN / (TN + FN) < 0.001']
PASS T1 8d: node pages, NPV near 0: beta 8c's text written without "= <" (26 pages)   []
PASS T1 8d: the node page reads "prior < 0.001 (1 in 10000)" for a prior of 1 in 10,000

-- T2. a sweep: every other display is unchanged --
     160 states: 57 identical, 103 differ only by the rewrite; harness errors 0
       "1−PPV = < 0.001"  ->  "1−PPV < 0.001"
       "BayesTree: PPV = 0.910, NPV = < 0.001 (prior 0.999, sens 0.010, spec 0.010)"  ->  "BayesTree: PPV = 0.910, NPV < 0.001 (prior 0.999, sens 0.010, spec 0.010)"
       "BayesTree: PPV = 0.952, NPV = < 0.001 (prior 0.999, sens 0.010, spec 0.500)"  ->  "BayesTree: PPV = 0.952, NPV < 0.001 (prior 0.999, sens 0.010, spec 0.500)"
       "BayesTree: PPV = 0.995, NPV = < 0.001 (prior 0.999, sens 0.010, spec 0.950)"  ->  "BayesTree: PPV = 0.995, NPV < 0.001 (prior 0.999, sens 0.010, spec 0.950)"
       "BayesTree: PPV = 0.998, NPV = < 0.001 (prior 0.999, sens 0.500, spec 0.010)"  ->  "BayesTree: PPV = 0.998, NPV < 0.001 (prior 0.999, sens 0.500, spec 0.010)"
       "BayesTree: PPV = 0.999, NPV = < 0.001 (prior 0.999, sens 0.900, spec 0.010)"  ->  "BayesTree: PPV = 0.999, NPV < 0.001 (prior 0.999, sens 0.900, spec 0.010)"
       "BayesTree: PPV = < 0.001, NPV = 0.500 (prior 0.010, sens 0.010, spec 0.010)"  ->  "BayesTree: PPV < 0.001, NPV = 0.500 (prior 0.010, sens 0.010, spec 0.010)"
       "BayesTree: PPV = < 0.001, NPV = 0.980 (prior 0.010, sens 0.010, spec 0.500)"  ->  "BayesTree: PPV < 0.001, NPV = 0.980 (prior 0.010, sens 0.010, spec 0.500)"
       "BayesTree: PPV = < 0.001, NPV = 0.990 (prior < 0.001, sens 0.010, spec 0.010)"  ->  "BayesTree: PPV < 0.001, NPV = 0.990 (prior < 0.001, sens 0.010, spec 0.010)"
       "BayesTree: PPV = < 0.001, NPV = 0.995 (prior < 0.001, sens 0.500, spec 0.010)"  ->  "BayesTree: PPV < 0.001, NPV = 0.995 (prior < 0.001, sens 0.500, spec 0.010)"
       "BayesTree: PPV = < 0.001, NPV = 0.999 (prior < 0.001, sens 0.900, spec 0.010)"  ->  "BayesTree: PPV < 0.001, NPV = 0.999 (prior < 0.001, sens 0.900, spec 0.010)"
       "BayesTree: PPV = < 0.001, NPV = 1.000 (prior < 0.001, sens 0.010, spec 0.500)"  ->  "BayesTree: PPV < 0.001, NPV = 1.000 (prior < 0.001, sens 0.010, spec 0.500)"
     (74 distinct rewritten lines)
PASS T2 8d: 160 states of the tree, the summary and five views: beta 8d = beta 8c written without "= <"
PASS T2 8d: every rewritten line differs only by "= <" -> "<" or "= >" -> ">"

-- T3. bt_Eq --
PASS T3 8d: bt_Eq("0.161") = " = 0.161"
PASS T3 8d: bt_Eq("< 0.001") = " < 0.001"
PASS T3 8d: bt_Eq("> -0.001") = " > -0.001"
PASS T3 8d: bt_Eq("") = " = "
PASS T3 8d: bt_Eq("-0.250") = " = -0.250"
PASS T3 8d: bt_Eq("1/7") = " = 1/7"
PASS T3 8d: bt_FrLn('    = ', '', '< 0.001') = "    < 0.001  (decimal)" (beta 8c: "    = < 0.001  (decimal)")
PASS T3 8d: bt_FrLn('    = ', '', '0.161') = "    = 0.161  (decimal)" (beta 8c: "    = 0.161  (decimal)")
PASS T3 8d: bt_FrLn('ab', '', '< 0.001') = "ab< 0.001  (decimal)" (beta 8c: "ab< 0.001  (decimal)")
PASS T3 8d: bt_FrLn('', '', '0.5') = "0.5  (decimal)" (beta 8c: "0.5  (decimal)")
PASS T3 8d: bt_FrLn('    = ', '19/118', '0.161') = "    = 19/118 = 0.161" (beta 8c: "    = 19/118 = 0.161")
PASS T3 8d: bt_FrLn('    = ', '1/989902', '< 0.001') = "    = 1/989902 < 0.001" (beta 8c: "    = 1/989902 = < 0.001")

-- T4. the sites left alone --
       prior := MIN(0.999, MAX(0.0001, pr2));
       sens  := MIN(0.999, MAX(0.01, se2));
       spec  := MIN(0.999, MAX(0.01, sp2));
       sens  := MIN(0.999, MAX(0.001, tp/(tp+fn)));
       spec  := MIN(0.999, MAX(0.001, tn/(btFP+tn)));
       prior := MIN(0.999, MAX(0.0001, prior));
       sens := MIN(0.999, MAX(0.01, se2));
       spec := MIN(0.999, MAX(0.01, sp2));
PASS T4 8d: every assignment of sens, spec and prior from the student clamps to 0.999 at the top and 0.0001 (prior) or 0.01 / 0.001 (sens, spec) at the bottom (8 lines)
PASS T4 8d: sens, spec, prior are held to 0.001..0.999 by the input, so these never floor: sens >= 0.0010, spec >= 0.0010, 1 - sens >= 0.0010, 1 - spec >= 0.0010, 1 - prior >= 0.0010, LR+ = sens / (1 - spec) >= 0.0010, LR- = (1 - sens) / spec >= 0.0010
PASS T4 8d: accuracy = prior x sens + (1 - prior) x spec, so it is >= 0.001 over the whole box
PASS T4 8d: no line of the T2 sweep in beta 8d reads "= <" or "= >"
     kept, no "=" directly before the relation: e.g. []

-- T5. the code --
PASS T5 8d: the code lines removed are exactly the 38 display sites and bt_FrLn's two (37 lines)   []
PASS T5 8d: the code lines added are exactly those sites through bt_Eq, bt_FrLn's new branch and bt_Eq (49 lines)   []
PASS T5 8d: bt_Eq is called at 38 display sites, plus twice in bt_FrLn
PASS T5 8d: the new names bt_Eq and eqS are not built-ins (ignoring case), and bt_Eq carries the bt_ tag
PASS T5 8d: bt_Eq is defined once, after bt_fmtp and before its first use
PASS T5 8d: the banner reads "beta 8d", the tested-on lines name 8d, and a "beta 8d" paragraph is there
PASS T5 8d: no code line or display string names beta 8c (it is named only in comments)   []
     display strings naming a beta: none
PASS T5 8d: no display string names an older beta
PASS T5 8d: no // comment inside an open bracket (bracket depth 0 at every comment)   []
PASS T5 8d: INSTRING appears as often as in beta 8c (16); bt_Eq reads the first character only inside IF DIM(eqS) > 0
PASS T5 8d: every LOCAL line holds at most 6 names

== U (8e). the fleet review of 19 Oct 2026: B1, B2, B3, B5, B6, B7 (harness v3e) ==
-- U1 (B1): the diagnostic odds ratio --
PASS U1 8e (B1): the review's case (N 1000, prior 0.001, sens 0.9, spec 0.95; expected FN = 0.1): "diagnostic OR = 171.000   (LR+/LR−)" (LR+/LR- = (0.9/0.05)/(0.1/0.95) = 171.000)
PASS U1 8e (B1): ... beta 8d: "diagnostic OR = ∞   (no false negatives/positives)"
PASS U1 8e (B1): from the keys (defaults, - x6, More, Diag): ['diagnostic OR = 171.000   (LR+/LR−)']
PASS U1 8e (B1): ... beta 8d from the same keys: ['diagnostic OR = ∞   (no false negatives/positives)']
     320 states: beta 8d printed infinity in 215 of them; the cap note in 128
PASS U1 8e (B1): 320 states (prior 0.0001 .. 0.999, N 10 and 1000, sens and spec 0.01 .. 0.999): every DOR line = (sens x spec)/((1 - sens)(1 - spec)) at the shown precision (= TP x TN / (FN x FP)), the cap note exactly when sens or spec is 0.999, never infinity, and no other line changed   []
PASS U1 8e (B1): Enter counts 90 / 0 / 45 / 855 (sens clamped to 0.999): ['diagnostic OR = 18981   (sens or spec at its 0.999 cap)'] (= 18981)
PASS U1 8e (B1): ... beta 8d: ['diagnostic OR = ∞   (no false negatives/positives)']
PASS U1 8e (B1): direct call with cells (10, 0, 5, 85): "diagnostic OR = ∞   (sens or spec = 1)", no error
PASS U1 8e (B1): direct call with cells (10, 5, 0, 85): "diagnostic OR = ∞   (sens or spec = 1)", no error
PASS U1 8e (B1): direct call with cells (0, 10, 5, 85): "diagnostic OR = 0.000   (LR+/LR−)", no error
PASS U1 8e (B1): direct call with cells (10, 5, 85, 0): "diagnostic OR = 0.000   (LR+/LR−)", no error
-- U2 (B2): a flick --
PASS U2 8e (B2): a drag, then the same drag ending in a flick (x = 65535, or y = 65535 on the sens pair): the flick changes nothing -- slider prior 0.69 / 0.69, PPV view prior 0.32 / 0.32, ROC spec 0.68 / 0.68, sens drag 0.95 / 0.95
PASS U2 8e (B2): ... beta 8d: the flick sends the prior to 0.999 (slider and PPV view), spec to 0.01 and sens to 0.01 -- slider prior 0.69 / 0.999, PPV view prior 0.32 / 0.999, ROC spec 0.68 / 0.01, sens drag 0.95 / 0.01
-- U3 (B3): "D+ = N × prior" --
     prior 0.0001: beta 8e "D+ = N × prior = 1000 × 0.0001"; beta 8d ['D+ = N × prior = 1000 × < 0.001']
PASS U3 8e (B3): node pages D+ and "source", 10 priors (0.0001 .. 0.999, and a second test's 1.0E-6): the prior is a number -- 3 decimals from 0.001 up, else down to its first digit (4 at least) -- never "× <"; every other line unchanged   []
PASS U3 8e (B3): ... beta 8d read "1000 × < 0.001"   ['D+ = N × prior = 1000 × < 0.001']
PASS U3 8e (B3): the tree's D+ box at prior 0.0001: ['D+ = N × prior = 1000 × 0.0001']; with a second test (stage prior 1.0E-6): ['D+ = N × prior = 990 × 0.000001']
PASS U3 8e (B3): ... beta 8d: ['D+ = N × prior = 1000 × < 0.001'] and ['D+ = N × prior = 990 × < 0.001']
-- U4 (B5): NND --
PASS U4 8e (B5): J = 0.0004: "NND = undef (J near 0)   (1/J)" (beta 8d: "NND = undef (J ≤ 0)   (1/J)")
PASS U4 8e (B5): J = 0.0000: "NND = undef (J ≤ 0)   (1/J)"
PASS U4 8e (B5): J = -0.1000: "NND = undef (J ≤ 0)   (1/J)"
PASS U4 8e (B5): J = 0.0006: "NND = 1666.667   (1/J)"
PASS U4 8e (B5): J = 0.8500: "NND = 1.176   (1/J)"
-- U5 (B6): the prevalence with two tests --
PASS U5 8e (B6): two tests: "prevalence = 0.667  (test 1's PPV)"; back to one test: "prevalence = 0.100"
PASS U5 8e (B6): ... beta 8d: "prevalence = 0.667" and "prevalence = 0.100"
-- U6 (B7): Enter counts --
PASS U6 8e (B7): typed 10.8 / 9.2 / 45.5 / 854.9: the tree reads ['N = 918'] (10 + 9 + 45 + 854 = 918), prior 0.021 = 19/918
PASS U6 8e (B7): ... beta 8d: ['N = 920.4']
PASS U6 8e (B7): the form opens with the tree's whole counts: [90.0, 10.0, 45.0, 855.0] at the defaults, [1.0, 0.0, 50.0, 949.0] at prior 0.001; OK on it at the defaults keeps prior 0.1, sens 0.9, spec 0.95 (88 (prior 0.100, sens 0.900, spec 0.950))
     (at prior 0.001, OK on the whole counts 1 / 0 / 50 / 949 gives sens 0.999: no false negative left)
PASS U6 8e (B7): ... beta 8d opened with the expected counts [0.0, 0.0, 0.0, 0.0], [0.9, 0.1, 49.95, 949.05]
-- U7: the sweep of part T2, beta 8e against beta 8d --
     160 states: 54 identical, 106 differ; the differing lines: {'DOR': 100, 'D+': 32, 'NND': 10}
PASS U7 8e: 160 states (the tree, the summary, five views and 13 node pages each): beta 8e = beta 8d except the DOR, NND and "N × prior" lines, each of which is the listed change and checked by value   []
-- U9: the teaching-aid notice --
PASS U9 8e: help page 3 ("reading it forensically") shows "Teaching aid; not for casework or legal use." in red at y 197.0, inside x 316 by a 6.5-px model (ends 294.0), clear of the line above and the footer
PASS U9 8e: ... beta 8d has no such line
PASS U9 8e: the header carries the DISCLAIMER line beside the distribution terms
-- U8: the review's check_BayesTree.py (19 Oct 2026) on beta 8e and on beta 8d --
PASS U8: the review script ran 143 checks on each version
     beta 8d: 136 PASS, 7 FAIL; beta 8e: 141 PASS, 2 FAIL
     fixed:  A: DOR at prior 0.001, sens 0.9, spec 0.95 (N = 1000): the page shows ['diagnostic OR = ∞   (no false negatives/positive
     fixed:  A: reproduced from the keys (defaults, - x6, More, Diag): ['diagnostic OR = ∞   (no false negatives/positives)']   prior
     fixed:  G: a flick on the slider (last move x = 65535, WaitLab) leaves the prior at 0.999 -- it jumps to the right end   (robust
     fixed:  G: the same flick in the PPV view leaves the prior at 0.999   (same guard)
     fixed:  G: the same flick in the ROC view leaves spec at 0.01 (not the 0.01 floor)   (same guard)
     fixed:  I: node page D+ at prior 0.0001 reads ['D+ = N × prior = 1000 × < 0.001'] ("1000 × < 0.001": the 8d pass fixed "= <", no
     open:   I: prior 0.001: NPV = 0.999895 prints as ['NPV = 1.000', 'NPV = 1.000'] (a sharp student: "NPV cannot be exactly 1 while
     recorded the old text, now changed: A: J = 0.0004 (> 0): NND line says ['NND = undef (J near 0)   (1/J)']   (cosmetic: says "J <= 0" for
PASS U8: every check beta 8d failed passes on beta 8e (6: the DOR twice, the three flicks, "× <"), except B4's "NPV = 1.000" (Roger's decision)
PASS U8: the one check that now "fails" recorded the old NND text ("J ≤ 0" for J = 0.0004), changed by B5

== G (8e). beta 8e against beta 8d ==
PASS G (8e): the routines that differ from beta 8d are exactly ['BayesTree', 'bt_DiagView', 'bt_HelpView', 'bt_NodeInfo', 'bt_PrevView', 'bt_RocView']   ['BayesTree', 'bt_DiagView', 'bt_HelpView', 'bt_NodeInfo', 'bt_PrevView', 'bt_RocView']
PASS G (8e): bt_HelpView: the notice on page 3, nothing else (Roger, 19 Oct 2026)
PASS G (8e): bt_NodeInfo: the two new LOCALs, niPS worked out once, and the two "N × prior" lines; nothing else
PASS G (8e): bt_DiagView: the DOR from the rates, the NND line, the prevalence suffix; nothing else
PASS G (8e): bt_PrevView: the one guard on the drag
PASS G (8e): bt_RocView: the one guard on the drag
PASS G (8e): BayesTree(): the move block inside the on-screen guard (re-indented, otherwise unchanged), the Enter counts prefill and IP; nothing else
PASS G (8e): outside those routines only comments differ (file-level LOCALs, bt_fmtp, bt_Eq, bt_WaitAny, bt_SlP ... byte for byte in code)
PASS G (8e): the banner reads "BAYESTREE - beta 8e", the tested-on lines name 8e, and a "beta 8e" paragraph is there
PASS G (8e): no code line names a beta
PASS G (8e): every LOCAL line holds at most 6 names (max 6)
PASS G (8e): the new names niPS and niDg are not built-ins, ignoring case
PASS G (8e): no // comment inside an open bracket   []
PASS G (8e): INSTRING appears exactly as often as in beta 8d
PASS G (8e): no character outside the beta 8d set in code (the new code is ASCII apart from the × already used)
PASS R8 8c: bt_NumS(0.1,) = "0.1" (beta 8b: "0.1")   []
PASS R8 8c: bt_NumS(0.01,) = "0.01" (beta 8b: "0.01")   []
PASS R8 8c: bt_Pct(1.002002e-06,) = "0.000 %" (beta 8b: "1.002ᴇ-4 %")   []
PASS R8 8c: bt_Pct(0.00308,) = "0.31 %" (beta 8b: "0.31 %")   []
PASS R8 8c: bt_fmtq(0.001, 0.001) = "0.001" (beta 8b: "0.001")   []
PASS R8 8c: bt_fmtq(0.001, 0.001) = "0.001" (beta 8b: "0.001")   []
PASS R8 8c: bt_fmtq(0.0, 0.0002) = "< 0.001" (beta 8b: "< 0.001")   []
     harness runs of beta 8e in this script: 407; scientific STRING results: none
PASS R8 8c: no STRING(x, 2, d) of beta 8e printed in scientific form in any of the 407 runs   []
     (beta 8b in the same script: [(0.0001002002, 3, '1.002ᴇ-4')])

285 PASS, 0 FAIL
exit 0
