Skip to content

Commit b92e991

Browse files
hyperpolymathclaude
andcommitted
fix(idris): rebuild logSafeBounded proof for Idris2 0.8.0
The pre-existing `logSafeBounded` proof in SafeAPIKey.idr did not type-check on the bundled Idris2 0.8.0. Audit-by-build (not by reading PROOF-NEEDS.md) was the only way to spot this. Three concrete defects, each fixed independently here: 1. **Removed local `plusLteMonotone` helper** which wrapped `lteTransitive (plusLteMonotoneRight _ lmn) (plusLteMonotoneLeft _ lpq)`. Idris2 0.8.0's `Data.Nat.plusLteMonotone` is exactly the shape we want (`LTE m n -> LTE p q -> LTE (m + p) (n + q)`), so the local wrapper is redundant. The wrapper called `lteTransitive` (no longer in stdlib — it's now `Control.Relation.transitive` and ambiguous in this import context) and passed wildcard arguments that didn't match the current `plusLteMonotoneRight` / `plusLteMonotoneLeft` signatures (`(p,q,r : Nat) -> LTE q r -> LTE (q+p) (r+p)`). Using stdlib directly sidesteps all of it. 2. **Lifted both branches out of the `with`-block.** Inside the with-block, the elaborator does not reduce `length "***"` (or `length "..."`) at type level — the goal is exposed as `LTE (integerToNat (prim__cast_IntInteger (prim__strLength (if ...)))) 11` with the `if`-arm unreduced. Outside the with-block the same lemmas type-check via `Refl`-equivalent computation. The fix: extract `logSafeBoundedShort` and `logSafeBoundedLong` as private helpers and let the with-block just dispatch. 3. **Right-associated the long-path proof.** `(++)` is right- associative in Idris (`a ++ b ++ c = a ++ (b ++ c)`), but the original proof bound `(p1 ++ "...") ++ p3` (left-associative) while the with-block's `False`-branch rewrites the goal to `substr 0 4 s ++ ("..." ++ substr (...) 4 s)`. The new proof composes `step23 (length "..." + length (substr ...) ≤ 3 + 4)` then `step123 (length (substr 0 4 s) + length (...) ≤ 4 + 7)`, producing exactly the right-associated form. Also fixed two bound-name typos in `toLogSafeShortEq` and `toLogSafeLongEq` where the outer pattern discarded the `prf` argument that the `False`/`True` branch then referenced (`absurd prf` against an undefined `prf`). Build verification (per-module `--check`, full ipkg --build hangs on this machine — see PR #108 notes): Boj.SafetyLemmas green Boj.SafeAPIKey green (this PR) Boj.Safety green Boj.APIContractCoverage green Boj.CartridgeDispatch green Boj.Catalogue green Boj.CredentialIsolation green Boj.Federation green Boj.SafeCORS green Boj.SafeHTTP green Boj.SafePromptInjection green Boj.SafeWebSocket green Stacked on PR #108 (`feat/item11-proof-debt-honest-framing`) because this proof depends on SafetyLemmas's comma-fix; when #108 merges, this auto-rebases to main. No new axioms introduced. The 5-class-(J) framing from PR #108 is preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0221f4e commit b92e991

1 file changed

Lines changed: 59 additions & 21 deletions

File tree

src/abi/Boj/SafeAPIKey.idr

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -145,23 +145,74 @@ sufficientEntropyNonEmpty (MkSufficientEntropy s {prf}) with (unpack s) proof up
145145
-- Works because toLogSafe is transparent and `if True then x else y = x`.
146146
private
147147
toLogSafeShortEq : (s : String) -> (length s <= 8 = True) -> toLogSafe s = "***"
148-
toLogSafeShortEq s _ with (length s <= 8)
148+
toLogSafeShortEq s prf with (length s <= 8)
149149
toLogSafeShortEq _ _ | True = Refl
150150
toLogSafeShortEq _ prf | False = absurd prf
151151

152152
-- Reduce toLogSafe s to the concat form when the long-path condition holds.
153153
private
154154
toLogSafeLongEq : (s : String) -> (length s <= 8 = False) ->
155155
toLogSafe s = substr 0 4 s ++ "..." ++ substr (length s `minus` 4) 4 s
156-
toLogSafeLongEq s _ with (length s <= 8)
156+
toLogSafeLongEq s prf with (length s <= 8)
157157
toLogSafeLongEq _ prf | True = absurd prf
158158
toLogSafeLongEq _ _ | False = Refl
159159

160-
-- Monotonicity of + over LTE (derived from Data.Nat primitives).
160+
-- Length bound on the redacted-short marker. Lifted out of `logSafeBounded`'s
161+
-- with-block so the elaborator reduces `length "***"` against the witness
162+
-- `LTE 3 11`. Inside the with-block the reduction does not fire on Idris2
163+
-- 0.8.0 (the goal is exposed as
164+
-- `LTE (integerToNat (prim__cast_IntInteger (prim__strLength (if ...))))`
165+
-- and the `if`-arm is not reduced before unification).
161166
private
162-
plusLteMonotone : {m, n, p, q : Nat} -> LTE m n -> LTE p q -> LTE (m + p) (n + q)
163-
plusLteMonotone lmn lpq =
164-
lteTransitive (plusLteMonotoneRight _ lmn) (plusLteMonotoneLeft _ lpq)
167+
shortMarkerBounded : LTE (length "***") 11
168+
shortMarkerBounded = LTESucc (LTESucc (LTESucc (LTEZero {right = 8})))
169+
170+
-- Length bound on the long-path ellipsis marker. Same lift-out reason as
171+
-- `shortMarkerBounded`.
172+
private
173+
ellipsisLen3 : LTE (length "...") 3
174+
ellipsisLen3 = LTESucc (LTESucc (LTESucc LTEZero))
175+
176+
-- Short path of `logSafeBounded`, lifted out of the with-block. The return
177+
-- type uses the *literal* `"***"` rather than `toLogSafe s`, which matches
178+
-- what the with-block's True-branch substitution rewrites the goal to.
179+
private
180+
logSafeBoundedShort : (s : String) -> (length s <= 8 = True) ->
181+
LTE (length "***") 11
182+
logSafeBoundedShort _ _ = shortMarkerBounded
183+
184+
-- Long path of `logSafeBounded`, lifted out of the with-block. The return
185+
-- type carries the substr-form directly (matching what the with-block False-
186+
-- branch rewrites the goal to via `if False`-evaluation of `toLogSafe`). The
187+
-- substr arguments are repeated rather than let-bound because Idris2 0.8.0's
188+
-- elaborator doesn't always propagate `let`-binding equalities through
189+
-- `appendLengthSum`'s implicit arguments. The proof is right-associated to
190+
-- match `++`'s associativity (`a ++ b ++ c = a ++ (b ++ c)`).
191+
private
192+
logSafeBoundedLong : (s : String) -> (length s <= 8 = False) ->
193+
LTE (length (substr 0 4 s ++ "..." ++ substr (length s `minus` 4) 4 s)) 11
194+
logSafeBoundedLong s _ =
195+
let l1 : LTE (length (substr 0 4 s)) 4
196+
l1 = substrLengthBound s 0 4
197+
l3 : LTE (length (substr (length s `minus` 4) 4 s)) 4
198+
l3 = substrLengthBound s (length s `minus` 4) 4
199+
-- Length of "..." ++ p3
200+
eq23 : length ("..." ++ substr (length s `minus` 4) 4 s)
201+
= length "..." + length (substr (length s `minus` 4) 4 s)
202+
eq23 = appendLengthSum "..." (substr (length s `minus` 4) 4 s)
203+
step23 : LTE (length "..." + length (substr (length s `minus` 4) 4 s)) (3 + 4)
204+
step23 = plusLteMonotone ellipsisLen3 l3
205+
l23 : LTE (length ("..." ++ substr (length s `minus` 4) 4 s)) 7
206+
l23 = replace {p = \n => LTE n 7} (sym eq23) step23
207+
-- Length of p1 ++ ("..." ++ p3)
208+
eq123 : length (substr 0 4 s ++ "..." ++ substr (length s `minus` 4) 4 s)
209+
= length (substr 0 4 s) + length ("..." ++ substr (length s `minus` 4) 4 s)
210+
eq123 = appendLengthSum (substr 0 4 s) ("..." ++ substr (length s `minus` 4) 4 s)
211+
step123 : LTE (length (substr 0 4 s) + length ("..." ++ substr (length s `minus` 4) 4 s)) (4 + 7)
212+
step123 = plusLteMonotone l1 l23
213+
l123 : LTE (length (substr 0 4 s ++ "..." ++ substr (length s `minus` 4) 4 s)) 11
214+
l123 = replace {p = \n => LTE n 11} (sym eq123) step123
215+
in l123
165216

166217
||| The redacted key from `toLogSafe` is always at most 11 characters long.
167218
|||
@@ -173,21 +224,8 @@ plusLteMonotone lmn lpq =
173224
export
174225
logSafeBounded : (s : String) -> LTE (length (toLogSafe s)) 11
175226
logSafeBounded s with (length s <= 8) proof cond
176-
logSafeBounded s | True =
177-
let eq = toLogSafeShortEq s cond
178-
in replace {p = \t => LTE (length t) 11} (sym eq)
179-
(LTESucc (LTESucc (LTESucc (LTEZero {right = 8}))))
180-
logSafeBounded s | False =
181-
let p1 = substr 0 4 s
182-
p3 = substr (length s `minus` 4) 4 s
183-
eq = toLogSafeLongEq s cond
184-
l1 = substrLengthBound s 0 4
185-
l3 = substrLengthBound s (length s `minus` 4) 4
186-
eq12 = appendLengthSum p1 "..."
187-
eq123 = appendLengthSum (p1 ++ "...") p3
188-
l12 = replace {p = \n => LTE n 7} (sym eq12) (plusLteMonotoneRight 3 l1)
189-
l123 = replace {p = \n => LTE n 11} (sym eq123) (plusLteMonotone l12 l3)
190-
in replace {p = \t => LTE (length t) 11} (sym eq) l123
227+
logSafeBounded s | True = logSafeBoundedShort s cond
228+
logSafeBounded s | False = logSafeBoundedLong s cond
191229

192230
--------------------------------------------------------------------------------
193231
-- FFI Bridge Declarations

0 commit comments

Comments
 (0)