Skip to content

fix(idris): rebuild logSafeBounded proof for Idris2 0.8.0 (SafeAPIKey baseline rot) - #116

Merged
hyperpolymath merged 2 commits into
mainfrom
fix/safeapikey-baseline-rot
May 20, 2026
Merged

fix(idris): rebuild logSafeBounded proof for Idris2 0.8.0 (SafeAPIKey baseline rot)#116
hyperpolymath merged 2 commits into
mainfrom
fix/safeapikey-baseline-rot

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Restores Boj.SafeAPIKey to a green Idris2 0.8.0 build. The pre-existing logSafeBounded proof on main does not type-check; this PR rebuilds it against the current stdlib + elaborator behaviour. No new axioms. The 5-class-(J) framing established in #108 is preserved.

Discovered while verifying #108 (item 11). Was originally stacked on #108; rebased onto main after #108 merged at 2026-05-20T08:13Z.

Three independent defects, each fixed

  1. Removed the redundant local plusLteMonotone helper. It wrapped lteTransitive (plusLteMonotoneRight _ lmn) (plusLteMonotoneLeft _ lpq):

    • lteTransitive is no longer in stdlib — Idris2 0.8.0 uses Control.Relation.transitive, but that's ambiguous in this import context.
    • plusLteMonotoneRight / plusLteMonotoneLeft have signature (p, q, r : Nat) -> LTE q r -> LTE (q+p) (r+p) in this stdlib — the wrapper passed wildcards expecting a different shape.

    The stdlib already ships Data.Nat.plusLteMonotone : LTE m n -> LTE p q -> LTE (m + p) (n + q) — exactly what the wrapper was trying to be. Use it directly.

  2. Lifted both branches out of the with-block. Inside logSafeBounded s with (length s <= 8) proof cond, Idris2 0.8.0 does not reduce length "***" (or length "...") at type level — the goal is exposed as LTE (integerToNat (prim__cast_IntInteger (prim__strLength (if True then ... else ...)))) 11 with the if-arm unreduced. Outside the with-block, the same lemmas type-check via Refl-equivalent computation. Extracted logSafeBoundedShort / logSafeBoundedLong as private helpers; the with-block now just dispatches.

  3. Right-associated the long-path proof. (++) is right-associative in Idris (a ++ b ++ c = a ++ (b ++ c)), but the original composed (p1 ++ "...") ++ p3 (left-associative) — its conclusion didn't match the with-block's False-branch goal. New proof: step23 (length "..." + length (substr ...) ≤ 3 + 4) then step123 (length (substr 0 4 s) + length (...) ≤ 4 + 7).

Plus two bound-name typos in toLogSafeShortEq / toLogSafeLongEq where the outer _ discarded the prf argument that the inner branch then referenced.

Test plan

Per-module idris2 --check on every safety module — all 12 green:

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

idris2 --build src/abi/boj.ipkg does not complete in 9 min on my dev box (gets SIGTERMed) — same note as #108. Per-module --check is the verification surface.

Why this matters

PROOF-NEEDS.md's 2026-05-18 audit claims SafeAPIKey carries constructive proofs that close BJ2-partial. Pre-this-PR the proof did not compile. The audit was, like the SafetyLemmas axiom count noted in #108, evidently a desk-read of the source rather than a build. With this PR merged, the audited safety surface is buildable from scratch.

Refs epic #87 Tier C (companion to #108 / #109).

🤖 Generated with Claude Code

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>
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 31 issues detected

Severity Count
🔴 Critical 19
🟠 High 5
🟡 Medium 7

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Python file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/.github/scripts/validate-eclexiaiser.py",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/ephapax-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath marked this pull request as ready for review May 20, 2026 08:46
@hyperpolymath
hyperpolymath merged commit 1e60f60 into main May 20, 2026
15 of 20 checks passed
@hyperpolymath
hyperpolymath deleted the fix/safeapikey-baseline-rot branch May 20, 2026 08:46
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 31 issues detected

Severity Count
🔴 Critical 19
🟠 High 5
🟡 Medium 7

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Python file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/.github/scripts/validate-eclexiaiser.py",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/ephapax-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

hyperpolymath added a commit that referenced this pull request May 20, 2026
Both merged after PR #126 was opened.

CHANGELOG.md `[Unreleased]` Fixed:
  - Added bullet for SafeAPIKey `logSafeBounded` rebuild (#116, MERGED
    2026-05-20T08:46Z).
  - Added bullet for `tests/aspect_tests.sh` grep-count bash bug (#118,
    MERGED 2026-05-20T09:02Z).

.machine_readable/6a2/STATE.a2ml session-history:
  - Updated the 2026-05-20 entry to mark #116 and #118 MERGED inline,
    rather than the stale OPEN/OPEN-DRAFT status they carried when the
    entry was written.

#123 (e2e.yml Zig + Deno pin refresh) remains OPEN — listed in
session-history with its current status.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 20, 2026
…eep (#126)

## Summary

Human + machine documentation close-out for the 2026-05-20 epic #87 Tier
C session and follow-on baseline-rot sweep. No code, no behavioural
change.

## Files

- **`CHANGELOG.md`** `[Unreleased]`:
- *Added* — ADR-0014 RFC (#109); README "Formal verification" section
(#108).
- *Fixed* — SafeAPIKey `logSafeBounded` proof rebuild for Idris2 0.8.0
(#116); `tests/aspect_tests.sh` grep-count bash bug (#118); honest
framing of the ABI axiom count + `(s t : T)` → `(s, t : T)` parser-comma
fix to `SafetyLemmas.idr` (#108).
- **`.machine_readable/6a2/STATE.a2ml`**:
  - `last-updated` bumped `2026-04-25` → `2026-05-20`.
- `believe-me-count` corrected `4` → `5`, with citation back to
`PROOF-NEEDS.md` 2026-05-18 audit + #108.
- `[session-history]` entry appended summarising items 11 + 12 + the
SafeAPIKey baseline-rot follow-up + the CI baseline-rot sweep +
operational notes (parallel-session branch drift, workflow OAuth scope
dance).

## Companion PRs from this thread

| PR | Status | What |
|---|---|---|
| #108 | MERGED 2026-05-20T08:13Z | item 11 honest framing (docstring +
README + parser-comma) |
| #109 | MERGED 2026-05-20T08:12Z | item 12 ADR-0014 RFC
(cross-cartridge composition safety) |
| #116 | MERGED 2026-05-20T08:46Z | SafeAPIKey `logSafeBounded` rebuild
for Idris2 0.8.0 |
| #118 | MERGED 2026-05-20T09:02Z | `tests/aspect_tests.sh` grep-count
bash bug |
| #123 | OPEN | `.github/workflows/e2e.yml` Zig + Deno pin refresh |
| **#126 (this)** | OPEN | session-close docs |

## Out of scope (intentionally)

- `governance / Language / package anti-pattern policy` baseline rot
stays untouched per the parallel-session-branch-drift guardrail (parent
campaign standards#66 owns it).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jun 24, 2026
…orker (#251)

Proof-completion work across two layers. Both pieces share this branch
because the session is pinned to it; they are logically independent and
reviewable as separate commits.

---

## 1 — ABI: discharge `charEqSym` axiom → constructive theorem
(`666c3e0`)

`charEqSym : (x, y : Char) -> (x == y) = (y == x)` was a class-(J)
`believe_me` axiom classified "genuinely unavoidable." It isn't — it is
**derivable from `charEqSound`** (which the repo already has):

- `x == y = True` → `charEqSound` gives `x = y`, collapsing both sides →
both `True`;
- a mixed `True`/`False` split is impossible (soundness forces the
contradiction);
- both `False` → trivially equal.

No new axiom is introduced. This is the first `(a) DISCHARGED` entry
under the trusted-base reduction policy (standards#203), dropping the
sanctioned count **5 → 4**. Survivors (`charEqSound`, `unpackLength`,
`appendLengthSum`, `substrLengthBound`) remain genuinely irreducible
over opaque `Char`/`String` primitives.

**Verified:** `cd src/abi && idris2 --typecheck boj.ipkg` → 17/17
modules clean (Idris2 0.8.0, Chez 9.5.8); `bash
scripts/check-trusted-base.sh` → OK, 4 axioms. (Same run confirms
`Boj.CartridgeDispatch`/BJ1 still type-checks.)

Count reconciled across `check-trusted-base.sh` (`EXPECTED_AXIOMS` 5→4),
`PROOF-NEEDS.md`, `docs/proof-debt.md` (charEqSym → §(a)), `STATE.a2ml`,
and `docs/backend-assurance/prim__eqChar.md`. Also fixed pre-existing
drift in `src/abi/README.adoc`: it wrongly listed `logSafeBounded` as a
believe_me (constructive since #116), said "graded D" (READINESS says
**C**), and called CartridgeDispatch "WIP / not in `boj.ipkg`" (it's
in-package and type-checks).

## 2 — Elixir: TLA+ model of `BojRest.JsWorker` (`836c977`)

`specs/elixir-harness/` — a TLA+ model of
`elixir/lib/boj_rest/js_worker.ex` (GenServer pipelining concurrent
requests to one persistent Deno Port, per-request 30 s timeout,
crash-replies-all + `:one_for_one` restart). Closes the "Elixir harness
has no formal coverage" gap on the proof axis (the Idris2 proofs stop at
the Zig FFI boundary).

**Verified with TLC** (`Requests = {r1,r2,r3}`, 341 states, no error):

- **ReplyOnce** — no caller replied twice under any
response/timeout/crash interleaving (the `Map.pop` guard). Headline.
- **Consistent**, **NoPendingWhileDown** — crash clears `pending`
atomically.
- **EventuallyReplied** (liveness) — the 30 s timer guarantees
termination even if Deno hangs.
- Non-vacuity: `ReachOk/ReachTimeout/ReachCrashed` sanity controls all
*refuted* (all terminal outcomes reachable).

Deferred (noted in the README): the `JsWorkerPool` routing/supervision
layer, and the `Invoker` pool once it exists (it's fork-per-request
today — ADR-0005/0006).

---

## Scope

Items 1 + 2 (Idris ABI) and item 4 (Elixir model). Zig FFI/invariants
(item 3) is out of scope (separate workstream). Also carries `ef959f0`
(prior `allTake` dedup, not yet on `main`). Neither piece moves the CRG
grade — both are on the formal-proof axis, not the empirical/dogfooding
axis.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01XrPAh7eBSUcVKauTVdXH9Y

---
_Generated by [Claude
Code](https://claude.ai/code/session_01XrPAh7eBSUcVKauTVdXH9Y)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant