1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// SHIP-TWO-001 §6 Compound Ship Gates — GATE-SHIP-007 algorithm-level
// PARTIAL discharge.
//
// Spec: docs/specifications/aprender-train/ship-two-models-spec.md §6 row
// `GATE-SHIP-007 | No unwrap() in new code (enforced by .clippy.toml) |
// merge`.
// Contract: contracts/compound-ship-gates-v1.yaml v1.1.0 PROPOSED
// (FALSIFY-GATE-SHIP-007 — wired in the same PR as this file lands).
//
// GATE-SHIP-007 is the *merge-time* hygiene gate for introduced `.unwrap()`
// calls. `.clippy.toml` lists `unwrap` under `disallowed-methods`, so
// `cargo clippy -- -D warnings` fails on any offending line. The tool-
// emitted lint produces an integer "offending-call count" — this gate
// says that integer MUST be zero in new code.
//
// This file discharges the *decision rule* at `PARTIAL_ALGORITHM_LEVEL`:
// given a u32 count of offending `.unwrap()` calls, the verdict is `Pass`
// iff `count == 0` and `Fail` otherwise. The tool-level portion
// (actually running `cargo clippy -- -D warnings` on the new-code diff
// and parsing the lint output) is intentionally out of scope here; what
// this file proves is that the compound gate's *threshold shape*
// (zero-tolerance) cannot be silently relaxed to "≤ N" without breaking
// this test.
//
// Zero-tolerance shape: `u32` precludes negative inputs by construction.
// A boundary mutation `count == 0 → count < N` would still Fail this
// test for any N > 0 at count=1. Symmetric twin of GATE-SHIP-010
// (advisory count) at the same `0` floor.
/// Maximum tolerated count of newly-introduced `.unwrap()` calls in a
/// diff. Zero-tolerance: any `.unwrap()` in new code blocks merge.
///
/// Derivation: `docs/specifications/aprender-train/ship-two-models-spec.md`
/// §6 row GATE-SHIP-007 + `.clippy.toml` `disallowed-methods` entry for
/// `unwrap`. Changing this value from 0 is a contract amendment that
/// requires a spec update, a contract bump, and a CLAUDE.md §
/// "unwrap() banned" policy change.
pub const AC_GATE_SHIP_007_MAX_TOLERATED_UNWRAP_COUNT: u32 = 0;
/// Binary verdict for FALSIFY-GATE-SHIP-007 / GATE-SHIP-007.
/// `Pass` iff `count == 0`. `Fail` otherwise (any non-zero count is a
/// merge-blocker per `.clippy.toml`).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GateShip007Verdict {
/// No `.unwrap()` calls introduced in the diff. The
/// `cargo clippy -- -D warnings` disallowed-methods rule passes.
/// Merge gate is green.
Pass,
/// At least one `.unwrap()` call was introduced in the diff.
/// Merge is blocked until every call is rewritten as `expect(…)`
/// or `ok_or_else(|| ...)?`.
Fail,
}
/// Algorithm-level verdict rule for FALSIFY-GATE-SHIP-007 /
/// GATE-SHIP-007: zero-tolerance `.unwrap()` count threshold.
///
/// Zero-tolerance rationale: `.clippy.toml` bans `.unwrap()` via the
/// `disallowed-methods` registry; a single call is a -D warnings lint
/// error and fails CI. The integer "offending-call count" emitted by
/// clippy must therefore be zero. Softening to "≤ N" would silently
/// allow drift one-PR-at-a-time.
///
/// # Examples
///
/// ```
/// use aprender::format::gate_ship_007::{
/// verdict_from_unwrap_count, GateShip007Verdict,
/// };
///
/// // No .unwrap() in new code → Pass.
/// assert_eq!(
/// verdict_from_unwrap_count(0),
/// GateShip007Verdict::Pass
/// );
///
/// // Single .unwrap() → Fail (merge blocked).
/// assert_eq!(
/// verdict_from_unwrap_count(1),
/// GateShip007Verdict::Fail
/// );
/// ```
#[must_use]
pub const fn verdict_from_unwrap_count(count: u32) -> GateShip007Verdict {
if count == AC_GATE_SHIP_007_MAX_TOLERATED_UNWRAP_COUNT {
GateShip007Verdict::Pass
} else {
GateShip007Verdict::Fail
}
}
// ─────────────────────────────────────────────────────────────
// Unit tests — FALSIFY-GATE-SHIP-007 algorithm-level proof
// ─────────────────────────────────────────────────────────────
#[cfg(test)]
mod gate_ship_007_tests {
use super::*;
/// FALSIFY-GATE-SHIP-007 algorithm-level PARTIAL discharge: prove
/// the zero-tolerance `.unwrap()` count threshold. Any edit that
/// softens `== 0` to `<= N` for any positive N must break this
/// test before a merge.
#[test]
fn falsify_gate_ship_007_unwrap_zero_tolerance() {
// Section 1: zero-count boundary → Pass. The happy path: a
// clean diff with no new `.unwrap()` calls.
assert_eq!(
verdict_from_unwrap_count(0),
GateShip007Verdict::Pass,
"count = 0 must Pass (clean diff)",
);
// Section 2: one-count adjacent boundary → Fail. The sharpest
// counter-example: a single `.unwrap()` must Fail. A softening
// from `== 0` to `<= 1` would flip this to Pass.
assert_eq!(
verdict_from_unwrap_count(1),
GateShip007Verdict::Fail,
"count = 1 must Fail (single .unwrap() blocks merge)",
);
// Section 3: clear Fail band — various multi-unwrap counts.
// Catches any relaxation to a mid-range tolerance.
for &n in &[2_u32, 10, 100, u32::MAX] {
assert_eq!(
verdict_from_unwrap_count(n),
GateShip007Verdict::Fail,
"count = {n} must Fail (zero-tolerance band)",
);
}
// Section 4: monotonicity sweep 0..=256 — exactly one input
// yields Pass (count = 0). This catches mutations that flip
// the inequality direction (e.g., `!= 0` → `>= 0` would Pass
// all counts).
for n in 0_u32..=256 {
let verdict = verdict_from_unwrap_count(n);
let expected = if n == 0 {
GateShip007Verdict::Pass
} else {
GateShip007Verdict::Fail
};
assert_eq!(
verdict, expected,
"count = {n} expected {expected:?}, got {verdict:?}",
);
}
// Section 5: provenance pin — zero-tolerance is load-bearing
// and lockstepped with `.clippy.toml` + CLAUDE.md. If the
// tolerance is ever softened (e.g., migrating to a code-smell
// budget), this constant and every consumer must move
// together.
assert_eq!(
AC_GATE_SHIP_007_MAX_TOLERATED_UNWRAP_COUNT, 0,
"max tolerated .unwrap() count is 0 \
(spec §6 GATE-SHIP-007; .clippy.toml disallowed-methods)",
);
}
}