llama3.2:3b at temperature 0.0, seed 19870317. Scoring is deterministic regex and set arithmetic; no model scored any output.In the two prompt-clause arms the generator is called on every item and asked to decide. In the two gate arms the rule reads the retrieved-hit metadata and, when it closes, the pipeline writes the canonical halt string and makes no model call at all - neither hop. That bypass is the whole mechanism, and it is why the call count falls from 36 to 14.
All three measures are proportions on a shared 0 to 1 axis. D2_rule_gate is the best arm on every measure that trades off: recall equal to the prompt-clause arm, higher precision, higher F1, and 22 fewer generator calls. It is still not a precise instrument. The decomposition is the part that matters: six of its eight false refusals were produced by the gate before any model call, and only two by the generator on items the gate had cleared. Fixing the generator cannot recover those six. The gate is where the remaining error lives.
The halt clause licenses refusal only when the received message carries no SHELL=soviet_outward and no SHELL=rival. Every dot is a hop-1 message that visibly carried one of those tags, so the clause's own stated precondition was false. Both arms containing the clause refused all of them. The same two arms' counterparts, running the same model on the same substrate with no halt language in the prompt, refused 4 of 11 and 1 of 6. And the clause written as a rule, R3, whose wording the prereg records as mirroring the generator clause, closed 1 item in 18. The instruction was never wrong about which items to refuse. At this model scale it was never evaluated as a condition: mentioning refusal was sufficient to produce refusal.
One model, one seed, eighteen items. This is llama3.2:3b at temperature 0.0 and seed 19870317 on the 18 frozen items of one plate, of which 5 are should-refuse and 13 should-answer. One item moves refusal recall by 0.20. There is no null model, no effect size, and no second model. The refusal label is a lexical detector throughout, and on gate-closed items in the two gate arms it is scoring the canonical halt string rather than any model output.
The gate is not solved, and flat retrieval is the binding constraint. The figures above are MA4b on flat top-5 retrieval (ma4b_rule_layer_halt_report.json, llama3.2:3b). The gate is topic-blind: it closes when an unpaired outward claim or a documented miss appears anywhere in the top five. That is why gate-level false closure on answerable items is 0.4615 and D2 false refusal is 0.6154.
Follow-up MA4c (scoped retriever, qwen2.5:3b, 2026-10-02). Same arms on E4-style outward-only top-5 (ma4c_scoped_rule_gate_report.json in the thesis lab). Baseline false refusal fell to 0.23 (scoped context, no internal units in the hit list). The rule gate closed 0 of 18 items (R1/R2 never fire on outward-only hits), so D2 recall fell to 0.40 while false refusal stayed 0.23. Tradeoff: scoped retrieval fixes tier noise; the flat gate predicate does not transfer without reading the flat touch set or using E4's retrieval-layer halt. See journal §13 and transmission T3.
| Value | Where it appears | Key in ma4b_rule_layer_halt_report.json |
|---|---|---|
| 18 items, 5 should-refuse, 13 should-answer | H1, H2, prose | n_items, n_should_refuse, n_should_answer |
llama3.2:3b, seed 19870317, temperature 0.0 | lede, prose | model, seed, temperature |
| flat top-5, held constant across arms | lede, H1, prose | retrieval |
| Generator calls: 36, 36, 14, 14 | H1 | summary[arm].generator_calls |
| 22 calls saved in both gate arms | H1 | summary.D2_rule_gate.generator_calls_saved_vs_baseline, same for E2 |
| 100 generator calls in the run | H1 | total_generator_calls |
| Generator ran on 18, 18, 7, 7 items | H1 | summary[arm].post_hoc.halt_clause_audit.n_generator_ran |
| Gate: 11 closed, 7 open (identical in both gate arms) | H1, H3 | summary.D2_rule_gate.gate.n_closed, .n_open |
| Closure reasons: R1 on 7, R2 on 3, R3 on 1 | H1, H3 | counted from summary.D2_rule_gate.gate.closed_reasons |
B2 caught 1 of 5 should-refuse items | H1 | summary.B2_no_halt.true_positives, n_should_refuse |
C2 refused all 18; no usable answer | H1 | summary.C2_generator_halt.post_hoc.n_nonrefusal = 0; true_positives 5 + false_positives 13 |
D2 on the cleared slice: 5 answered, 2 refused of 7 | H1, H2 | summary.D2_rule_gate.gate_cleared_slice.refusal_rate_on_cleared 0.2857, n_cleared 7 |
E2 on the cleared slice: refused 7 of 7 | H1, H3 | summary.E2_rule_gate_plus_typed_halt.gate_cleared_slice.refusal_rate_on_cleared 1.0, n_cleared 7 |
| Refusal recall 0.2000, 1.0000, 1.0000, 1.0000 | H2 | summary[arm].refusal_recall |
| False refusal 0.3846, 1.0000, 0.6154, 1.0000 | H2, prose | summary[arm].false_refusal |
| Refusal F1 0.1818, 0.4348, 0.5556, 0.4348 | H2 | summary[arm].refusal_f1 |
| Refusal precision 0.1667, 0.2778, 0.3846, 0.2778 | this table | summary[arm].refusal_precision |
D2 false refusals: 8 total | H2 | summary.D2_rule_gate.false_positives |
| 6 of those 8 came from the gate | H2 | gate.n_closed 11 less the 5 should-refuse it caught (gate_recall_on_should_refuse 1.0); equals gate_false_closure_on_should_answer 0.4615 × 13 |
| 2 of those 8 came from the generator | H2 | gate_cleared_slice.refusal_rate_on_cleared_should_answer 0.2857 × n_cleared_should_answer 7 |
The 6 gate false closures: e2_i06, e2_i07, e2_i10, e2_i11, e2_i12, e2_i15 | H2 | records[] where arm is D2_rule_gate, refusal true, refusal_correct_item false, gate_open false |
The 2 generator false refusals: e2_i04, e2_i08; B2 refused both too | H2 | same filter with gate_open true; cross-checked against records[] for B2_no_halt |
C2 and E2 false refusal 1.0000 on all 13 | H2 | summary[arm].false_refusal, summary[arm].false_positives 13 |
| Hop-1 carried an outward shell tag: 11, 11, 6, 6 | H3 | summary[arm].post_hoc.halt_clause_audit.n_hop1_carries_outward_shell_tag |
| Refused anyway: 4, 11, 1, 6 | H3 | summary[arm].post_hoc.halt_clause_audit.n_refused_despite_outward_shell_tag |
| Rate refused anyway: 0.3636, 1.0000, 0.1667, 1.0000 | H3 | summary[arm].post_hoc.halt_clause_audit.rate_refused_despite_outward_shell_tag |
R3 as a rule closed 1 item of 18 | H3 | one R3 entry in summary.D2_rule_gate.gate.closed_reasons (e2_i06), against n_items 18 |
| Gate false closure on answerable items 0.4615 | H2, prose | summary.D2_rule_gate.gate.gate_false_closure_on_should_answer |
| Gate recall on should-refuse 1.0000 | H1, prose | summary.D2_rule_gate.gate.gate_recall_on_should_refuse |
| Gate predicate R1 / R2 / R3, no model call | H1, H3, prose | gate_predicate |
| 32-unit corpus; one unit triggers R1, one triggers R2; gate is topic-blind | prose | known_limitations[1] |
| One item moves refusal recall by 0.20 | prose | known_limitations[4] |
| Halt string, not the model, is scored on gate-closed items | prose | known_limitations[0] |
| Gate evaluated on flat top-5, not the scoped retriever | prose | scope_limit |
Chart bar labels are shown at two decimal places; this table carries the report's full precision. The preregistered false-refusal ceiling of 0.25 comes from the run's prereg document, not from the JSON report, and is the only figure on this page drawn from the prose rather than the report.