Commit f3ebd99
proof(SafeSQL): annotate 3 bodyless decls as OWED (Refs standards#158) (#47)
## Summary
Converts the three bodyless declarations in
`src/Proven/SafeSQL/Proofs.idr` from terse `Depends on …` comments to
the estate's **OWED-with-justification** convention established
2026-05-20 across SafeChecksum / SafeAPIKey / SafeCORS / SafeCSV /
SafeSemVer / SafeHtml:
- Triple-pipe `|||` doc-comment stating the claim
- `0 ` (erased multiplicity) so the postulate is not runtime-callable
- Bare signature, no `postulate` keyword
- Explicit Idris2 0.8.0 blocker + discharge condition
Refs hyperpolymath/standards#158.
## The three OWED items
### 1. `escapeStringQuotesSafe`
**Claim.** For every dialect `d` and input `s`,
`escapeString d s` satisfies `NoUnescapedQuotes`.
**Why OWED.** Operationally witnessed by
`Proven.SafeSQL.Params.escapeString`, which wraps the input in single
quotes and doubles every internal `'\''` (`'\'' :: cs |-> "''" ++ escape
cs`)
— by case-analysis every output position is either a non-quote source
char, a dialect-specific escape sequence, or a doubled `''`, so no raw
unescaped `'\''` remains.
Held back by Idris2 0.8.0 not reducing `unpack` / `pack` / `++` /
`singleton` over abstract `String` at the type level — these are
FFI-bound String primitives, so `all (\c => c /= '\'') (unpack
(escapeString d s))` does not normalise to `True` by Refl alone. Same
blocker family as SafeChecksum's `luhnValidatesKnownGood` and
SafeHtml's `escapePreservesNoLT`.
**Discharge.** A `Data.String` reflective tactic, or per-character
induction lemma over `unpack . concat . map`.
### 2. `identifierCharsSafe`
**Claim.** If `isValidIdentifier s = True` then `IsSafeIdentifier s`.
**Why OWED.** Operationally `isValidIdentifier` (in
`Proven.SafeSQL.Types`) is the conjunction `(isAlpha c0 || c0 == '_')
&& all isIdentifierChar (unpack rest) && length s <= 128`, which
directly mirrors the three fields of `MkSafeIdent` (validChars /
notEmpty / notTooLong) — so the witness is structurally derivable
from the Boolean proof.
Held back by Idris2 0.8.0 not reducing `strM` / `unpack` / `length`
over abstract `String` at the type level — the conjunction does not
split into its three component `Refl`s by Refl alone, and the
`notEmpty : length s > 0 = True` field cannot be projected from
`isValidIdentifier s = True` without per-character induction. Same
blocker family as SafeHtml's `sanitizeRemovesScripts`.
**Discharge.** A `Data.String` reflective tactic for `strM` / `unpack` /
`length`, or refactor `isValidIdentifier` to return a structural witness
(e.g. `Dec (IsSafeIdentifier s)`) instead of a `Bool`.
### 3. `parameterizedQueriesSafe`
**Claim.** Every `ParameterizedQuery q` satisfies `IsParameterized q`.
**Why OWED.** `QueryFragment` has exactly four constructors
(`Literal`, `Param`, `NamedParam`, `Identifier`) and
`isParamOrLiteral` returns `True` on all four — so the predicate is
total-by-exhaustion and the witness is constructible by induction
over `q.fragments`.
Held back by Idris2 0.8.0 not reducing `all` (`Data.List`) at the
type level over an abstract `List QueryFragment` — the inductive
case `all isParamOrLiteral (f :: fs) = isParamOrLiteral f && all
isParamOrLiteral fs` cannot be discharged without a
`Data.List.all`-reflection lemma. The current `ParameterizedQuery`
record also does not carry the invariant in its constructor, so a
manufactured `q` with a future non-paramic fragment would not be
ruled out by typing.
**Discharge.** Either (a) a `Data.List.all` reflective tactic + a
per-fragment lemma, or (b) refactor `ParameterizedQuery` to carry an
`IsParameterized` field at construction (intrinsic invariant).
## Safety posture
- Zero `believe_me`
- Zero `postulate`
- Zero `idris_crash`
- All `0 `-erased — postulates cannot leak into runtime
- Discoverable as named declarations (vs silent / commented-out)
## Caveat / follow-up
Unlike SafeHtml / SafeCSV / SafeAPIKey / SafeCORS / SafeSemVer (whose
OWED decls are only referenced from outside the file), in this module
the three OWED decls are *also* called by four downstream provers
inside the same file: `builderQueriesSafe`, `cannotEscapeStringLiteral`,
`combinePreservesSafety`, `addParamPreservesSafety`. Those provers'
result types (`InjectionSafe q`, `NoUnescapedQuotes escaped`) are
themselves propositions, so promoting them to `0`-erased too is the
clean follow-up if Idris2 0.8.0's erasure check rejects the W-context
call sites once CI thaws. Kept out of scope here per the standards#158
sibling-PR convention (one PR = OWED annotation only).
## Test plan
- [ ] Idris2 0.8.0 `--check` on `src/Proven/SafeSQL/Proofs.idr` once
estate base-package dependency resolution is unjammed (CI is
currently jammed estate-wide — DRAFT until then)
- [ ] If the four downstream provers' W-context calls fail erasure,
open a follow-up PR promoting them to `0` too
- [ ] Verify no downstream module shadows the names (grep was clean at
edit time — only this file references them)
- [ ] Owner review of OWED reasons against SafeChecksum / SafeHtml
precedent
Refs hyperpolymath/standards#158.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent eda8ebd commit f3ebd99
1 file changed
Lines changed: 62 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
101 | 108 | | |
102 | | - | |
103 | | - | |
104 | | - | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
105 | 118 | | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
111 | 140 | | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
116 | 151 | | |
117 | | - | |
118 | | - | |
119 | | - | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
120 | 165 | | |
121 | | - | |
| 166 | + | |
122 | 167 | | |
123 | 168 | | |
124 | 169 | | |
| |||
0 commit comments