Skip to content

feat(proof): backend-assurance harness for prim__strToCharList (2/4) — campaign complete#140

Merged
hyperpolymath merged 2 commits into
mainfrom
feat/backend-assurance-str-to-char-list
May 21, 2026
Merged

feat(proof): backend-assurance harness for prim__strToCharList (2/4) — campaign complete#140
hyperpolymath merged 2 commits into
mainfrom
feat/backend-assurance-str-to-char-list

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Closes the backend-assurance campaign for epic #87 Tier C. With
this slice merged alongside #129 (slice 1/4) and #139 (slices 3/4 +
4/4), all 5 class-(J) believe_me axioms in
src/abi/Boj/SafetyLemmas.idr have external evidence.

Discharges external evidence for the final class-(J) axiom:

Site Axiom Type
:218 unpackLength (s : String) -> length (unpack s) = length s

The in-language proof does not change. The %unsafe /
believe_me site stays in source. The harness shrinks the trusted
base from "we trust the backend" to "we read the lowering and
randomly tested the operation against the property".

Artefacts (matching the #129 / #139 template)

  • elixir/test/backend_assurance/prim_str_to_char_list_test.exs
    BEAM property tests over the legal codepoint space (surrogates
    excluded). 4 properties + 1 boundary table:
    • length preservation: length(String.to_charlist(s)) == String.length(s)
    • empty-string corner: String.to_charlist("") == []
    • round-trip identity: to_string(to_charlist(s)) == s
    • charlist element-type sanity (legal codepoints, no surrogates)
  • docs/backend-assurance/prim__strToCharList.md — trusted-extraction
    prose. Chez argued from R6RS §6.7 (string model) + §11.12
    (string->list); BEAM argued from UTF-8 prefix-free codepoint
    counting via Elixir String.to_charlist/1 + String.length/1.
  • docs/backend-assurance/README.md — coverage-table row flipped
    from pending.
  • PROOF-NEEDS.md — backend-assurance evidence column filled for
    unpackLength. Class column updated to J ✓ marker (convention
    introduced by feat(proof): backend-assurance harness for prim__strAppend (3/4) + prim__strSubstr (4/4) #139). Site line number corrected from :211 to
    :218 (the file has shifted since the 2026-05-18 audit; previous
    PRs didn't sync this row).

The existing CI workflow .github/workflows/backend-assurance.yml
already path-filters on elixir/test/backend_assurance/** and
docs/backend-assurance/**, so it picks up the new test module
automatically — no workflow change in this PR.

Constraints honoured

Per docs/backend-assurance/README.md:

  • No new believe_me. External evidence only.
  • No in-language discharge. Ruled out by 2026-05-18 audit.
  • Two backends. Chez argued from R6RS; BEAM exercised by tests.
  • Honest framing. Surrogates excluded; normalisation out of
    scope and documented; round-trip property guards against a
    backend whose charlist conversion drops or duplicates codepoints
    in a way that happens to preserve length.

Test plan

  • Elixir parse-check on the new test file (syntax-only, via
    Code.string_to_quoted!)
  • BEAM operation smoke on boundary strings — 9 strings
    (empty, ASCII, Latin-1, CJK, astral, max codepoint, etc.); direct
    String.to_charlist/1 + String.length/1 + to_string calls
    outside the test harness. Length preservation and round-trip
    identity both hold across all 9.
  • CI backend-assurance workflow: BEAM property tests green
  • CI: existing workflows unaffected (no path-filter collisions)

Note: this session could not run mix test --only backend_assurance
locally — the asdf elixir 1.18.4-otp-25 install on the work
machine is empty (only .mix archives, no binary), and system
elixir 1.14 fails the project's ~> 1.15 requirement. Same gap
pattern slices 1/4 and 3/4+4/4 followed; CI is the gate.

Expected conflicts with #139

Both this PR and #139 branch off origin/main (per saved
stacked-PR-avoidance feedback), so they share a base. Conflicts on
merge are limited and trivial:

File Conflict shape Resolution
docs/backend-assurance/README.md This PR flips the prim__strToCharList row; #139 flips the prim__strAppend and prim__strSubstr rows. Different rows of the same table — line-level conflict only; accept both.
PROOF-NEEDS.md audit table This PR updates row 3; #139 updates rows 1/2/4/5 and adds the J ✓ explanation paragraph. Different rows; accept both.
PROOF-NEEDS.md remaining-axiomatic-surface table This PR updates unpackLength row; #139 updates the other four. Different rows; accept both.
PROOF-NEEDS.md standing note "Primitives validated so far" #139 bumps it to 3/4; reviewer should apply 4/4 (campaign complete) once both merge. Manual one-line edit during merge of whichever PR lands second.

The J ✓ marker is used on row 3 here even though the
explanation paragraph is contributed by #139 — post-#139-merge the
explanation is in place, and the marker's meaning is self-evident
from the value.

Heads-up for the reviewer

After this PR + #139 merge, the 4-slice backend-assurance harness is
complete. All 5 class-(J) axioms in SafetyLemmas.idr have
external evidence:

Future trusted-extraction work would only be needed if BoJ ships a
new codegen backend (e.g. JavaScript, native ML).

Refs #87 (Tier C backend-assurance campaign — closes the harness
sequence).

🤖 Generated with Claude Code

hyperpolymath and others added 2 commits May 21, 2026 10:06
Final slice of epic #87 Tier C backend-assurance campaign. Closes the
4-primitive sequence after #129 (prim__eqChar, slice 1/4) and #139
(prim__strAppend + prim__strSubstr, slices 3/4 + 4/4). With this
slice merged, all 5 class-(J) believe_me axioms in
src/abi/Boj/SafetyLemmas.idr have external backend-assurance
evidence — modulo any future backend (JS, native, etc.) that would
need a fresh trusted-extraction argument.

Discharges external evidence for one class-(J) believe_me axiom:

  :218  unpackLength : (s : String) -> length (unpack s) = length s

The in-language proof does NOT change. The %unsafe / believe_me site
stays in SafetyLemmas.idr. The harness shrinks the trusted base
from "we trust the backend" to "we read the backend lowering and
randomly tested the operation against the property".

Artefacts (matching the #129 / #139 template):

  - elixir/test/backend_assurance/prim_str_to_char_list_test.exs
    BEAM property tests via stream_data over UTF-8 binaries.
    Properties: length preservation, empty-string corner, round-trip
    identity (to_string ∘ to_charlist), charlist element-type sanity
    (legal codepoints, no surrogates), + boundary table covering all
    four UTF-8 encoding widths (ASCII / Latin-1 / CJK / astral).

  - docs/backend-assurance/prim__strToCharList.md
    Trusted-extraction prose: Chez (R6RS string->list + string-length,
    §6.7 + §11.12) and BEAM (Elixir String.to_charlist/1 + String.length/1
    over UTF-8, prefix-free codepoint counting).

  - docs/backend-assurance/README.md
    Coverage table flipped from _pending_ for prim__strToCharList.

  - PROOF-NEEDS.md
    Backend-assurance evidence column filled for unpackLength; class
    column updated to "J ✓" marker indicating externally-validated
    class-(J) status (convention introduced by #139). Site line
    number corrected from :211 to :218 (the file has shifted since
    the 2026-05-18 audit; the previous PR didn't sync this row).

The CI workflow .github/workflows/backend-assurance.yml already
path-filters on elixir/test/backend_assurance/** and
docs/backend-assurance/** so the new test module is picked up
automatically — no workflow change is needed.

Constraints honoured (from docs/backend-assurance/README.md):

  - No new believe_me. External evidence only; the axiom stays.
  - No in-language discharge. Ruled out by 2026-05-18 audit.
  - Two backends. Chez argued from R6RS; BEAM exercised by tests.
  - Honest framing. Surrogates excluded; normalisation out of scope
    and documented; round-trip property guards against a backend
    whose charlist conversion drops/duplicates codepoints in a way
    that happens to preserve length.

Test plan:

  - [ ] CI backend-assurance workflow: BEAM property tests green
  - [x] elixir parse-check on the new test file (syntax-only)
  - [x] BEAM operation smoke on boundary strings (9 strings:
        empty, ASCII, Latin-1, CJK, astral, max codepoint, etc.) —
        direct String.to_charlist/1 + String.length/1 + to_string
        calls outside the test harness. All pass; round-trip
        identity holds across all 9.
  - [ ] CI: existing workflows unaffected (no path-filter collisions)

Note: this session could not run `mix test --only backend_assurance`
locally — the asdf elixir 1.18.4-otp-25 install on this machine is
empty (only .mix archives, no binary), and system elixir 1.14 fails
the project's `~> 1.15` requirement. Same gap pattern slices 1/4 and
3/4+4/4 followed; CI is the gate.

**Expected conflicts with #139** (open at PR time of this branch):

  - docs/backend-assurance/README.md: this PR flips the
    prim__strToCharList row; #139 flips the prim__strAppend and
    prim__strSubstr rows. Different table rows, line-level conflict
    only.
  - PROOF-NEEDS.md: this PR updates row 3 (unpackLength) and the
    remaining-axiomatic-surface row for unpackLength. #139 updates
    rows 1/2/4/5 and the explanation paragraph introducing the
    "J ✓" marker convention. Trivial to resolve — the changes are
    in different rows.
  - PROOF-NEEDS.md standing note "Primitives validated so far":
    #139 bumps it to 3/4; once both merge, the bump should read 4/4
    (or simpler, "campaign complete"). Reviewer please apply during
    merge of whichever PR lands second.

The "J ✓" marker introduced by #139 is used here on row 3 even
though the explanation paragraph is contributed by #139 — the
convention's meaning is self-evident from the value alone, and
post-#139-merge the explanation is in place.

Refs #87 (Tier C backend-assurance campaign — closes the harness
sequence).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolve backend-assurance coverage conflicts after PR #139 and keep all four primitive harnesses listed as validated. Adjust BEAM harness checks to measure codepoint counts explicitly instead of Elixir grapheme counts.
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 21, 2026 13:20
@hyperpolymath hyperpolymath disabled auto-merge May 21, 2026 18:14
@hyperpolymath hyperpolymath merged commit 840b4b0 into main May 21, 2026
0 of 17 checks passed
@hyperpolymath hyperpolymath deleted the feat/backend-assurance-str-to-char-list branch May 21, 2026 18:15
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