<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/feed.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Rail</title>
  <subtitle>A self-hosting functional language. Updates from ledatic.org.</subtitle>
  <link href="https://ledatic.org/feed.xml" rel="self"/>
  <link href="https://ledatic.org/"/>
  <updated>2026-08-16T10:00:06Z</updated>
  <id>https://ledatic.org/</id>
  <author><name>zemo-g</name></author>
  <entry>
    <title>v5.1.0 — Rail emits its own GPU kernels</title>
    <link href="https://ledatic.org/changelog#v5.1.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v5.1.0</id>
    <updated>2026-05-15T00:00:00Z</updated>
    <summary>Major release.  Rail now generates Metal Shading Language source from
its op-DAG, JIT-compiles it via Metal's `newLibraryWithSource:`, and
dispatches the kernel at runtime.  Every kernel the GPU executes is
emitted by an attested Rail binary — the substrate piece needed for
end-to-end attested GPU training (see
[`rail-jit-fused-kernels-plan`](.) for the multi-month roadmap).</summary>
    <content type="html"><![CDATA[<p>Major release.  Rail now generates Metal Shading Language source from<br/>its op-DAG, JIT-compiles it via Metal's `newLibraryWithSource:`, and<br/>dispatches the kernel at runtime.  Every kernel the GPU executes is<br/>emitted by an attested Rail binary — the substrate piece needed for<br/>end-to-end attested GPU training (see<br/>[`rail-jit-fused-kernels-plan`](.) for the multi-month roadmap).</p><p>This release bundles the full GPU substrate that the auto-emission<br/>pipeline rests on: per-op Metal kernels, the bf16 numerics regime that<br/>unlocks stable 10k-step training, the JIT compile foundation, two<br/>hand-fused kernels, and the DAG matcher + emitter that drive them.</p><p>Auto-emission pipeline</p><p>• `stdlib/jit_node.rail` — JIT op-DAG types (`JitNode`, `TracedTensor`)<br/>  + tape primitives + `jit_nth`.  Pure-DAG module with no Tensor or<br/>  transformer dependency, so codegen consumers can import without<br/>  pulling the training stack.<br/>• `stdlib/jit_tape.rail` — execution tracers (`jit_leaf`,<br/>  `traced_rmsnorm`, `traced_matmul`) layered on top of jit_node.<br/>• `stdlib/jit_match.rail` — DAG matcher.  `walk_tape` returns a list<br/>  of `FuseMatch` records (`FuseRmsQKV`, `FuseSiluHad`) in tape order,<br/>  identifying subgraphs that fit known fusion shapes.<br/>• `stdlib/jit_emit.rail` — MSL emitter.  `emit_msl_for_match` takes<br/>  `(tape, FuseMatch)` and returns the kernel source.  Stubbed against<br/>  known patterns today; v5.2+ will replace with shape-parameterized<br/>  codegen driven by JitNode data.<br/>• `stdlib/jit.rail` — compile/dispatch shim.  No longer owns MSL<br/>  text; `jit_compile_*` pulls strings from `jit_emit`.  External API<br/>  unchanged for existing consumers.</p><p>Hand-fused Metal kernels</p><p>• `fused_rmsnorm_qkv` — RMSNorm + 3 matmul (Q/K/V) in one<br/>  threadgroup-per-row dispatch.  Exports Q | K | V | rstd | LN1 from<br/>  a single packed buffer (`4*SEQ*D + SEQ` floats); rstd and LN1 are<br/>  kept for backward.  4 dispatches → 1.  35× faster than the<br/>  per-op chain at training shapes (seq=512, d=64).<br/>• `fused_silu_hadamard` — SiLU(gate) * up in one elementwise<br/>  dispatch.  Exports h_act | sigmoid(gate) (`2*N` floats); sigmoid<br/>  is kept for backward.  18× faster than the per-op chain.</p><p>JIT compile foundation</p><p>• `tgl_jit_compile_from_tmp_file` — Metal's `newLibraryWithSource:`<br/>  driven from a Rail-emitted `.metal` file.  Returns a pipeline ID<br/>  cached in `g_jit_pipes` for reuse across steps.<br/>• `tgl_jit_dispatch_1in1out` / `tgl_jit_dispatch_2in1out` /<br/>  `tgl_jit_dispatch_rmsnorm_qkv` / `tgl_jit_dispatch_silu_hadamard` —<br/>  per-pattern dispatchers with f64↔f32 host staging.</p><p>Per-op GPU kernels</p><p>• `tgl_rmsnorm_save_f64` (1.8× over CPU at training shapes)<br/>• `tgl_rope_apply_f64` (7× over CPU)<br/>• `tgl_silu_fwd_f64` (19× over CPU)</p><p>Per-op wins translated to ~2% per-step at training shapes — confirms<br/>fusion (not per-op throughput) is the real ceiling, which is why the<br/>JIT pipeline above is the load-bearing thesis.  See<br/>`rail-gpu-fused-ops-2026-05-14` for the bench breakdown.</p><p>bf16 numerics regime</p><p>• `tgl_matmul_bf16` + `matmul_bf16` Rail wrapper.  bf16 has f32's<br/>  exponent range, so it sidesteps fp16's step-2759 NaN cliff<br/>  (`rail-bf16-stable-10k-2026-05-14`).  Training scripts default to<br/>  forward bf16 with f64 on embedding + LM-head + backward.</p><p>Training scripts (chunked-corpus sampler, 2-block d=64)</p><p>• `tools/train/lm_v3_chunked_bf16_full_long.rail` — bf16 forward,<br/>  10k-step stable, ~40% wall under f64 baseline.<br/>• `tools/train/lm_v3_chunked_jit_long.rail` — same architecture, Q/K/V<br/>  + SwiGLU run through fused JIT'd kernels.  200-step matched-seed<br/>  pilot vs bf16 baseline: trajectory shape preserved, no NaN, both<br/>  converge.  Wall-clock bench (3×3 alternating runs): 2.85%<br/>  step-throughput improvement over baseline at seq=512 d=64 d_ff=192.<br/>• `tools/train/lm_v3_chunked_fp16_attn_f64_long.rail` — falsification<br/>  experiment ruling out attention as the fp16 culprit<br/>  (`rail-fp16-attn-f64-falsified-2026-05-14`).</p><p>Tests + benches</p><p>• `tools/test/jit_kernel_smoke.rail` — Phase 0: Rail-emitted MSL<br/>  JIT-compiles + dispatches (4/4 silu values).<br/>• `tools/test/jit_tape_smoke.rail` — 9-node DAG construction with<br/>  shared parent (the QKV fusion signal).<br/>• `tools/test/jit_fused_qkv_smoke.rail` — numerical parity vs<br/>  per-op chain (max diff 1.67e-6, f32 floor).<br/>• `tools/test/jit_silu_hadamard_smoke.rail` — numerical parity<br/>  (max diff 1.5e-8).<br/>• `tools/test/jit_block_integration_smoke.rail` — both kernels<br/>  inside one block forward; rstd / ln1 / sigmoid parity all GREEN.<br/>• `tools/test/jit_dag_match_smoke.rail` — DAG matcher (5/5).<br/>• `tools/test/jit_emit_smoke.rail` — MSL emitter (4/4).<br/>• `tools/test/gpu_fused_ops_smoke.rail` — per-op kernel parity.<br/>• `tools/bench/jit_fused_qkv_bench.rail` — fused vs unfused at<br/>  training shapes.<br/>• `tools/bench/transformer_fused_ops_bench.rail` — per-op GPU vs CPU.</p><p>Stability</p><p>• 140/140 compiler test suite still green.<br/>• 2-pass byte-identical self-bootstrap unchanged (this release adds<br/>  stdlib + foreign decls + Metal sources; the compiler core is<br/>  untouched).</p>]]></content>
  </entry>
  <entry>
    <title>v5.0.2 — Attestation pipeline goes fully pure-Rail</title>
    <link href="https://ledatic.org/changelog#v5.0.2" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v5.0.2</id>
    <updated>2026-05-15T00:00:00Z</updated>
    <summary>Patch release. The first Rail release attested end-to-end through the
Rail substrate — no `curl`, no `shasum`, no Python anywhere in the
attestation path.</summary>
    <content type="html"><![CDATA[<p>Patch release. The first Rail release attested end-to-end through the<br/>Rail substrate — no `curl`, no `shasum`, no Python anywhere in the<br/>attestation path.</p><p>Fixes</p><p>• `stdlib/file.rail`: `foreign fopen path mode -&gt; int` (`084791f`).<br/>  `fopen` returns a file descriptor, not a `FILE*`. Declaring it as<br/>  `ptr` bypassed Rail's tagging and tripped a polymorphic untag for odd<br/>  fds (3 → 1, i.e. stdout). Corrected the foreign return type.<br/>• runtime `_fopen`: unwrap path + mode before `open(2)` (`95d81de`).<br/>  Wrapped Rail strings carry their `ptr` at the heap header, so `open()`<br/>  saw byte `0x09` (the header tag) as the path. `_fopen` now calls<br/>  `_str_unwrap`, matching the existing `_rail_read_file` contract.<br/>  Closes the argv-vs-literal path bug.</p><p>Attestation</p><p>• `tools/attest/`: shell escape hatches retired (`bbda5dd`).<br/>  `attest.sh`, `sign_attestation.sh`, and `publish.sh` deleted.<br/>  `release_index.rail` replaces the Python heredoc in<br/>  `attest_release.sh`. `attest.rail` + `publish.rail` are now canonical.</p><p>Stability</p><p>• New seed `rail_native` (`3b89d0f5`) is at the 2-pass byte-identical<br/>  fixed point.</p>]]></content>
  </entry>
  <entry>
    <title>v5.0.1 — Attestation hygiene + codegen tightening</title>
    <link href="https://ledatic.org/changelog#v5.0.1" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v5.0.1</id>
    <updated>2026-05-15T00:00:00Z</updated>
    <summary>Patch release. No new features.</summary>
    <content type="html"><![CDATA[<p>Patch release. No new features.</p><p>Codegen</p><p>• `arm64`: close the half-applied compile-fixes patch. `emit_x1`<br/>  large-immediate path, `emit_x1` global-`V` fallback, `O`-handler RHS<br/>  exclusion, and `?`-handler global-`V` exclusion. Self-compile fixed<br/>  point and test suite unchanged from the v5.0.0 baseline.</p><p>Attestation</p><p>• Backfill v4.0.0 / v4.0.1 / v4.1.0 release attestations —<br/>  previously tagged-but-unattested.<br/>• `.gitignore`: whitelist `releases//rail_native.attestation.json`<br/>  so a new release can't silently lose that file to the `rail_native.*`<br/>  wildcard rule.<br/>• `docs/RELEASES.md` — operational release runbook + 6 known gotchas.</p><p>Known limitation</p><p>• `attest.rail` still blocked on the `ftell` FFI bug, so attestation in<br/>  this release (including this release's own artifacts) still uses the<br/>  documented `tools/attest/attest.sh` shell escape hatch. Closed in<br/>  v5.0.2.</p>]]></content>
  </entry>
  <entry>
    <title>v5.0.0 — Self-hosted toolchain (Linux ELF substrate)</title>
    <link href="https://ledatic.org/changelog#v5.0.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v5.0.0</id>
    <updated>2026-05-14T00:00:00Z</updated>
    <summary>Major release.  Rail produces its own aarch64 Linux ELF binaries —
encoder, assembler, static linker, and ELF writer are all pure Rail.
On the supported subset of inputs, the build pipeline invokes no
external `as`, `ld`, or `codesign`.</summary>
    <content type="html"><![CDATA[<p>Major release.  Rail produces its own aarch64 Linux ELF binaries —<br/>encoder, assembler, static linker, and ELF writer are all pure Rail.<br/>On the supported subset of inputs, the build pipeline invokes no<br/>external `as`, `ld`, or `codesign`.</p><p>What ships</p><p>Built on top of v4 Phase 0–4b (encoder, Mach-O writer, ad-hoc<br/>codesigner, assembler-text → Mach-O driver) with three new modules:</p><p>| Module | Lines | Role |<br/>|---|---|---|<br/>| `jit/arm64.rail` | +200 | 23 new encoders for the Linux mnemonic set: `ldrb`/`strb` (imm, reg-offset, post-index), `clz`, `neg`, `cmn` (imm + reg), `rev`, `rev16`, `fneg`, `frinta`, `fcvt s_d` / `fcvt d_s`, `tbnz`, `stp`/`ldp` pre/post-index, `add`/`sub` immediate, `asr`/`lsr`/`lsl` immediate (sbfm/ubfm aliases) |<br/>| `stdlib/elf.rail` | 175 | `Elf64_Ehdr` + program-header writer for static aarch64 binaries; `elf_emit_tiny` (1 PT_LOAD R+X) + `elf_emit_full` (PT_LOAD text RX + PT_LOAD data RW with BSS via memsz &gt; filesz) |<br/>| `tools/v5/elf_asm.rail` | 567 | Section-aware ARM64 assembler + static linker.  Two-pass scan: pass 1 collects section sizes + `(name, section, offset)` labels; caller resolves to virtual addresses via layout; pass 2 emits bytes with `adrp` / `:lo12:` symbol resolution.  Handles `.text` / `.data` / `.bss` / `.rodata` / `.section __DATA,__mod_init_func` (skipped), `.quad` / `.byte` / `.long` / `.ascii` / `.asciz` / `.space` / `.comm` / `.p2align` / `.align`, plus the writeback `stp` / `ldp` variants and the `adrp` + `add :lo12:` / `ldr [base, :lo12:]` / `str [base, :lo12:]` Linux-ELF symbol-load idiom |<br/>| `tools/v5/compile_elf.rail` | 30 | Tiny driver (single segment, no data).  Useful for exit-code-only smoke tests |<br/>| `tools/v5/compile_elf_full.rail` | 80 | Full driver: source `.s` → 3-pass pipeline → multi-segment ELF.  Patches `e_entry` to `_start` if present |</p><p>Plus 23 new encoder verifications wired into `jit/test_encoders_v5.rail`<br/>(31/31 byte-identical against canonical `as` + `objdump`).</p><p>Verified end-to-end on aarch64 Linux (Pi Zero 2 W via Tailscale)</p><p>| Program | ELF size | Result | Substrate exercised |<br/>|---|---|---|---|<br/>| `tools/v5/exit42_linux.s` | 132 B | exit 42 | smallest valid static ELF |<br/>| `tools/v5/fib_linux.s` | 204 B | `exit(fib(10)) = 55` | function calls, `stp`/`ldp` pre/post-index writeback, conditional branches |<br/>| `tools/v5/hello_linux.s` | 4105 B | prints `"v5 lives\n"`, exit 9 | `adrp` + `add :lo12:`, `.data` segment, raw `write` syscall |<br/>| `tools/v5/bss_test_linux.s` | 4096 B | BSS counter loop, exit 7 | `.bss` segment, `.space N` reservation, BSS memsz &gt; filesz |</p><p>Each binary's `.text` bytes are byte-equivalent to canonical<br/>`as` + `ld` output for the same source.  Pipeline invokes neither<br/>external assembler nor linker.</p><p>compile.rail Linux pipeline hardening (precursor to substrate integration)</p><p>Two long-standing bugs in `build_linux` fixed:</p><p>• Duplicate-symbol awk strip gate dropped.  The macOS-emitted<br/>  runtime stubs (`_rail_print`, `_rail_print_float`, `_rail_shell`,<br/>  `_rail_arena_init`, `_rail_malloc_chain_drain`) were not being<br/>  stripped before concatenation with `tools/linux_libc.s` (which<br/>  redefines them via raw syscalls).  Result: `as` would reject the<br/>  combined `.s` with redefinition errors on both macOS-cross and Pi<br/>  native builds.  Strip list extended to cover them and the macOS-only<br/>  gate removed.<br/>• `.section __DATA,__mod_init_func` block stripped.  The macOS<br/>  constructor block (`.quad _rail_arena_init`) has no ELF analogue and<br/>  was triggering `as` errors at the section line.  New awk pass drops<br/>  the block on Linux.<br/>• `linux_libc.s` gains `_memcpy` (raw byte copy) and `_fmod`<br/>  (truncation-toward-zero double modulo) — the two libSystem references<br/>  Rail's compiler emits that had no Linux-side definition.  Sufficient<br/>  for a clean assemble + link of `compile.rail`-generated Linux output.</p><p>Self-compile fixed point preserved (round-to-round byte-identical).<br/>Test suite unchanged at 136/140 — the 4 `tensor_*` failures predate v5<br/>and trace to a missing `tools/metal/libtensor_gpu.dylib` artifact, not<br/>to v5 changes.</p><p>Substrate-thesis scope statement</p><p>v5.0 claims: Rail emits aarch64 Linux ELF binaries via the pure-Rail<br/>toolchain for the subset of ARM64 assembly emitted by compile.rail's<br/>Linux backend.  The new modules handle every mnemonic + addressing<br/>mode that appears in the canonical `.s` for any Rail program, including<br/>the full `tools/linux_libc.s` runtime body.</p><p>What is deferred:</p><p>• Pi self-host of `rail_native test` via the new toolchain.  The<br/>  current `.s` allocates ~1.2 GB of BSS for the young-generation GC,<br/>  which exceeds Pi Zero 2 W's 512 MB physical RAM.  Both canonical<br/>  `as` + `ld` and v5 pipeline produce semantically equivalent binaries<br/>  that share this limit.  Path forward (v5.1): heap-size configuration<br/>  knob in `build_linux` + bigger Linux test target.<br/>• macOS Mach-O end-to-end with dyld stubs.  Phase 4b's<br/>  `tools/v5/compile_macho.rail` covers the libSystem-free subset<br/>  (e.g. exit-code-only programs).  Stub-aware Mach-O — `LC_LOAD_DYLIB`,<br/>  indirect symbol table, `__stubs`, `__got` / `__la_symbol_ptr`,<br/>  bind-opcode stream — is roughly 1500 more lines.  Tracked as v5.2.</p><p>Tag-readiness checklist</p><p>• [x] `compile.rail` Linux output of a real Rail program traverses the<br/>      new pipeline → byte-equivalent ELF to canonical<br/>• [x] Linux ELF substrate verified on real aarch64 hardware (Pi)<br/>• [x] Encoders byte-verified against `as`: 89 + 56 + 31 = 176 total<br/>• [x] No regression on `./rail_native test` (136/140; 4 pre-existing)<br/>• [x] No regression on `./rail_native self` byte-identical fixed point<br/>• [x] CHANGELOG.md v5.0.0 entry (this section)<br/>• [x] Leak guard CI still passes<br/>• [ ] macOS dyld-stub path — deferred to v5.2</p>]]></content>
  </entry>
  <entry>
    <title>v4.1.0 — Repo hygiene + leak-guard CI</title>
    <link href="https://ledatic.org/changelog#v4.1.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v4.1.0</id>
    <updated>2026-05-13T00:00:00Z</updated>
    <summary>Minor release. Comprehensive cleanup pass over the public tree. No
compiled-binary change; no language or stdlib changes.</summary>
    <content type="html"><![CDATA[<p>Minor release. Comprehensive cleanup pass over the public tree. No<br/>compiled-binary change; no language or stdlib changes.</p><p>CI + leak-prevention (B1)</p><p>• New workflow `.github/workflows/leak-guard.yml` — every push and PR<br/>  is grep-scanned for the operator-recon pattern set (Tailscale IPs,<br/>  internal SSH targets, home-directory paths, internal Slack channel<br/>  IDs). Fails the build on any hit. Per-line opt-out via the comment<br/>  marker `leak-guard-allow`. CHANGELOG.md and the guard file are<br/>  excluded.<br/>• ci.yml triggers extended to include `next` branch and `v*` tags.<br/>  Test-count assertion generalised from hardcoded `137/137` to any<br/>  matching `N/N` (master is 137, next is 140, future may grow).<br/>• .gitignore — explicit ignores for `.mcp.json`, `.ledatic/`,<br/>  `.fleet/`, `*.pre-*`. Closes the casual-`git add` recurrence path<br/>  for the v4.0.1 leak class.</p><p>Branch hygiene (B2)</p><p>21 remote branches deleted from origin:</p><p>• 18 `feat/*` branches fully merged into `next` (security A/B/C lanes,<br/>  x86 conformance harness, x86 runtime extensions, JIT fixes, docs<br/>  refresh, auto-deploy, punch-list integration).<br/>• `jit` (merged into `next`).<br/>• `track-mhd-kernel` (merged into `master`).<br/>• `history-scrub-prep-2026-05-12` (unused experimental branch).</p><p>Remaining: `master`, `next`, `half-s2-kernels` (open compiler work),<br/>`compound/exp-008-bytes_to_str` (halted POC artifact). Down from 26<br/>branches to 4.</p><p>Doc pruning (B3)</p><p>~104 operator session-handoff files removed from the public tree:</p><p>• `docs/plans/` (74 files) — operator session-planning notes<br/>  (SESSION_HANDOFF_*, PROMPT_SESSION_*, WEEK_PLAN_*, PHASE_*, etc.).<br/>• `notes/` orphan files (12).<br/>• `docs/handoffs/` orphans (8).<br/>• `jit/` operator notes (9) — SCRATCH, CONTINUATION, SESSION_PROMPT*,<br/>  AGENT_DRY_RUN, NEXT_STAGES, closures, floats.<br/>• `SECURITY_HANDOFF.md` — internal Fort Knox punch list (the public<br/>  policy lives in `SECURITY.md`).</p><p>Kept: docs referenced from CHANGELOG (`notes/bootstrap_convergence_audit_*`,<br/>`notes/phase3_external_pilot_pitch_v0`); `jit/` code + README + CHANGELOG;<br/>`docs/sessions/` versioned handoffs (CHANGELOG-linked).</p><p>Dead-code pruning (B4)</p><p>• Deleted `tools/autocatalyst_v4.rail` (broken — referenced runtime/llm.o<br/>  which never landed in-tree, flywheel-v1 artifact).<br/>• Deleted `tools/ac_dashboard.rail` (orphan, flywheel dashboard).<br/>• Removed Razer3070 live-path references (decommissioned 2026-04-17):<br/>  - `tools/apps/control.rail` — Razer fleet row + curl status segment.<br/>  - `tools/fleet/fleet_display.rail` — razer_status/razer_iter/razer_max/<br/>    razer_ping/razer_loss + RAZER row in the SPI-LCD render.<br/>  - `tools/mcp/rail_mcp.rail` — tool_fleet_status no longer SSHes for<br/>    nvidia-smi / v6_train.log; description updated.<br/>  - `tools/compile.rail` — compile_x86 fallback message no longer<br/>    recommends scp-to-Razer; suggests cross-tools or native host.<br/>    Byte-identical bootstrap preserved.<br/>• `CLAUDE.md` target list: 'Linux x86_64 (Razer WSL)' →<br/>  'Linux x86_64 (cross-compile)'.</p><p>Structure pass (B5)</p><p>• Deleted 7 docs with no CHANGELOG or code references:<br/>  `RAIL_ENGINEER_PROMPT.md`, `flywheel-data-quality.md`,<br/>  `flywheel-world-research.md`, `cascade-training.md`,<br/>  `rail-plasma.md`, `railgpt-from-scratch.md`,<br/>  `self-improving-playbook.md`.<br/>• Flattened `docs/handoffs/` (down to a single entry after B3 prune):<br/>  `docs/handoffs/2026-05-02.md` → `docs/handoff-2026-05-02.md`.</p><p>README polish (B6)</p><p>• Badge: v3.0.0 → v4.0.0; tagline → "Substrate maturity".<br/>• Intro paragraph adds the v4.0.0 substrate-maturity lede (dual-backend<br/>  parity, JIT in Rail, 30/30 hard-bench, multi-witness attest).<br/>• New Releases section entry for v4.0.0 + a v4.0.1 sanitization note.<br/>• History table extended: 7 new rows spanning v3.7.0 → v4.0.1<br/>  (previously jumped from v3.0.0 to v2.23.0).</p><p>Verification</p><p>• Leak guard: 0 hits across tracked files for the union pattern set.<br/>• Test suite: 140/140 on the v4.1.0 tree (modulo the documented<br/>  `/tmp/rail_out` orphan-process collision when run concurrently with<br/>  another `rail_native test`).<br/>• `git push` on next: clean fast-forward; tag v4.1.0 cuts at 6 commits<br/>  past v4.0.1, all CI-green via the new workflow.</p>]]></content>
  </entry>
  <entry>
    <title>v4.0.1 — Public-surface sanitization</title>
    <link href="https://ledatic.org/changelog#v4.0.1" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v4.0.1</id>
    <updated>2026-05-13T00:00:00Z</updated>
    <summary>Patch release. Removes operator-specific infrastructure strings from the
public tree: Tailscale IPs, SSH usernames, home-directory paths, internal
Slack channel IDs, and a stray operator MCP config. No behavior change;
the compiled binary is identical to v4.0.0.</summary>
    <content type="html"><![CDATA[<p>Patch release. Removes operator-specific infrastructure strings from the<br/>public tree: Tailscale IPs, SSH usernames, home-directory paths, internal<br/>Slack channel IDs, and a stray operator MCP config. No behavior change;<br/>the compiled binary is identical to v4.0.0.</p><p>What was scrubbed (~110 files)</p><p>• Hard SSH targets in `tools/attest/*.sh`, `tools/fleet/*.sh`,<br/>  `tools/fleet/fleet_display.rail`, `tools/apps/control.rail` — replaced<br/>  with `&lt;witness-user&gt;@&lt;witness-host&gt;` / `&lt;peer-user&gt;@&lt;peer-host&gt;`<br/>  placeholders. Callers must supply real values via environment.<br/>• Tailscale IPs (`100.87.231.45`, `100.79.50.108`, `100.120.203.70`,<br/>  `100.109.107.54`, `100.109.63.37`) replaced with role placeholders<br/>  (`&lt;witness-tailscale-ip&gt;` etc.). Tailscale CGNAT-range addresses aren't<br/>  reachable from the public internet, but they were operational recon.<br/>• Home-directory paths (`/Users/ledaticempire/`, `/Users/user/`,<br/>  `/home/zemog/`) replaced with `~/` or `&lt;HOME&gt;` placeholders across<br/>  source, docs, `docs/plans/`, training fixtures, and Objective-C dispatchers.<br/>• Operator service files — `tools/fleet/witness.service`,<br/>  `tools/fleet/witness_push.service`, `tools/fleet/com.ledatic.*.plist`<br/>  renamed to `*.example` with `&lt;user&gt;` / `&lt;HOME&gt;` placeholders. Existing<br/>  install scripts already substitute these at install time.<br/>• Operator MCP config — `.mcp.json` removed from the tree. It was an<br/>  operator's Claude Code MCP wiring (path to `tools/mcp/rail_mcp.py`),<br/>  not a build artifact; the MCP server still runs locally with a<br/>  per-user `.mcp.json` outside the repo.<br/>• Slack channel IDs / DM names in `CHANGELOG.md`, `README.md`,<br/>  `stdlib/slack_client.rail` docblock, `docs/sessions/HANDOFF_v3_6.md` —<br/>  `D0ATHQ1BQD7` and `brockbro2` replaced with `&lt;DM_CHANNEL_ID&gt;` and<br/>  `&lt;test-dm&gt;`. Slack IDs don't grant access on their own, but these<br/>  were the only remaining specific-channel references in the public surface.</p><p>What was intentionally NOT scrubbed</p><p>• `reillygomez13@icloud.com` in `tools/deploy/gen_*.rail` — public<br/>  contact email rendered onto ledatic.org pages; meant to be public.<br/>• Commit messages in the v4.0.0 surface — rewriting history would break<br/>  existing clones for a topology-recon leak, not a credential leak.<br/>  The forward tree is clean; git history retains the originals.<br/>• `~/.ledatic/` path convention — generic project-named subdirectory,<br/>  not operator-specific.</p><p>Verification</p><p>```<br/>git grep -E "100\.(87|79|109|120)\.|zemog@|user@100|reillygomez@|\<br/>ledaticempire@|/Users/ledaticempire|/Users/user|/home/zemog|\<br/>Detro|D0ATHQ1BQD7|brockbro2"<br/>```<br/>→ empty across tracked files.</p><p>Why a patch release</p><p>v4.0.0 carried operator-recon strings inadvertently included via the<br/>multi-witness publisher work on the `next` lineage. The `master` lineage<br/>was scrubbed in `c4f6050` (2026-05-06) but `next` hadn't received the<br/>same pass. v4.0.1 brings the substrate-track tree to the same hygiene<br/>standard.</p>]]></content>
  </entry>
  <entry>
    <title>v4.0.0 — Substrate maturity</title>
    <link href="https://ledatic.org/changelog#v4.0.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v4.0.0</id>
    <updated>2026-05-13T00:00:00Z</updated>
    <summary>A major version bump tagged on the `next` lineage. (`master` continues the parallel
v3.x attestation/agent track; the two have diverged on purpose.) 216 commits since
`v3.11.0` was tagged on master 11 days ago — concurrency, playground, public JIT,
dual-backend parity, 30/30 substrate hard-bench publicly reproducible, browser-side
provenance verifier, four sweeping bug-class closures including a 17-day silent-
corruption fix discovered by a dual-implementation falsification harness.</summary>
    <content type="html"><![CDATA[<p>A major version bump tagged on the `next` lineage. (`master` continues the parallel<br/>v3.x attestation/agent track; the two have diverged on purpose.) 216 commits since<br/>`v3.11.0` was tagged on master 11 days ago — concurrency, playground, public JIT,<br/>dual-backend parity, 30/30 substrate hard-bench publicly reproducible, browser-side<br/>provenance verifier, four sweeping bug-class closures including a 17-day silent-<br/>corruption fix discovered by a dual-implementation falsification harness.</p><p>No public API breaks; the major bump is a positioning marker, not a SemVer surface<br/>change. The substrate-not-model thesis (`docs/site/jit.md` + `tools/bench/repro_30of30.sh`<br/>+ `https://ledatic.org/verify/&lt;id&gt;` + this entire shipping volume) is now publicly<br/>defensible without hand-waving.</p><p>What "substrate maturity" means here</p><p>• A frontier model + a 1KB Rail spec compiles 30/30 on a held-out hard-bench,<br/>  reproducible by any partner with an API key. (`f2c88b2`)<br/>• The compiler is genuinely self-hosted on two backends, each with full<br/>  same-bug-class parity for the 9 binary ops across both operand orderings.<br/>  (ARM64 140/140, x86_64 136/136. `9e16aa7` + `c9de6e9` + `b223960`.)<br/>• The verifier is a library, not a tool — `import "jit/grade.rail"` and a Rail<br/>  program can compile + execute new Rail at runtime in the same process. (`07366ea`)<br/>• The provenance pipeline is multi-witness Ed25519, browser-verifiable, with<br/>  pulse_id binding closing the prior session-replay gap. (`f732176` + `2ada525`)<br/>• A standalone single-file verifier ships at deterministic SHA — anyone can<br/>  grade reports without trusting the original signer's infrastructure.<br/>  (ledatic-site `8f5b928`)</p><p>Compiler &amp; runtime</p><p>• Concurrency v1. Typed channels + select over a pthread-backed runtime.<br/>  `import "stdlib/concurrent.rail"` exposes `rc_chan_make`/`rc_chan_send`/<br/>  `rc_chan_recv`/`rc_spawn`. int64-only values in v0; 9 + 8 falsification tests<br/>  green. (`4623e72`)<br/>• Auto-memo fib silent-corruption — FIXED. `compile.rail:2593` memo_store emit<br/>  was double-untagging x19 (which was already untagged in the prologue). Writes<br/>  went to `memo[n/2]` while reads keyed `memo[n]`; pairs collided on shared slots.<br/>  `fact` escaped because it has only one recursive call and never reads back; `fib`<br/>  failed because two recursive reads collide. `fib(10)` was returning `293886`<br/>  instead of 55. Found by the JIT REPL agent comparing shell-compile vs JIT on<br/>  the same program; one-line fix using x19 directly as the index register.<br/>  Falsification at `tools/test/auto_memo_fib_correctness.rail`. (`b89a60b`)<br/>• Nullary-LHS binary-op bug — FIXED. Any binary op with a top-level nullary<br/>  LHS expression was using the prior `x0` instead of the freshly-computed value.<br/>  `compile.rail::emit_x1` fast-path patched; 2-cycle bootstrap byte-identical.<br/>  Was the root of the multi-week "CPU substrate is mysteriously wrong" arc; closes<br/>  the substrate investigation. (pre-window but retroactively notable)<br/>• `_rail_join` O(n²) — FIXED. Runtime asm rewrite of join: 53.5 GB → 267 MB on<br/>  the 8×100K-float dump pattern (200× memory, 120× wall-clock). Diagnostic<br/>  harnesses kept at `tools/diagnose/dump_pattern_smoke.rail` +<br/>  `tools/diagnose/dump_bisect.rail`. (pre-window)<br/>• Same-bug-class parity sweep — CLOSED on both backends, both orderings.<br/>  Each of 9 binary ops (`+`, `-`, `*`, `/`, `%`, `&lt;`, `&gt;`, `&lt;=`, `&gt;=`) now has<br/>  symmetric handling for `(int, float)` and `(float, int)` operand orderings.<br/>  - x86 `(int, float)`: inline emit `check_both` + `.L&lt;op&gt;_mixed_if`. (`b223960`)<br/>  - x86 `(float, int)`: already covered by `b223960`'s symmetric routing.<br/>  - ARM64 `(int, float)`: inline emit `check_both` + `.L&lt;op&gt;_mixed_if` mirror.<br/>    (`9e16aa7` + `d4e3696`)<br/>  - ARM64 `(float, int)`: `.L&lt;op&gt;_mixed_fi` mirror that takes raw-f64 LHS via<br/>    `fmov d0, x1`, untags+converts tagged-int RHS via `asr` + `scvtf`. For<br/>    `_rail_add` specifically the dispatch is inserted at the top of `.Ladd_heap`<br/>    so the string-append path remains correct. (`c9de6e9`)<br/>  - 9 + 9 = 18 falsification tests at `tools/test/&lt;op&gt;_{int_float,float_int}_ordering.rail`.<br/>• 3-movk integer literal codegen. `emit_load_int` at `compile.rail:829` now<br/>  emits `movz` + up to 3 `movk` chunks (bits 0-15, 16-31, 32-47, 48-63) with zero<br/>  chunks at ≥#32 skipped, plus a symmetric `movn` + `movk` path for negatives.<br/>  `k16`/`k32`/`k48` computed via `shl 1 N` so constant-folding doesn't bake the<br/>  64-bit literal as a constant the seed can't emit. Regression tests `t132`/`t133`/`t134`.<br/>  ARM64 floor: 137 → 140. (`872424b`)<br/>• Bootstrap convergence audit — published. The "bootstrap doesn't converge"<br/>  claim was falsified: it's a 2-cycle limit cycle. gen0's shipped runtime asm<br/>  doesn't necessarily match what gen0's source emits, so cycle 1 typically differs;<br/>  gen2 always lands the byte-identical fixed point. See<br/>  `notes/bootstrap_convergence_audit_2026-05-13.md`.<br/>• Diagnostic surface. `strip_trailing_ws` helper replaces `trim` at 4 multi-line<br/>  `as`/`ld` result sites so undefined-symbol errors and assembly errors no longer<br/>  silently truncate to the first line. `shell_quote_arg` + `shell_quote_join` +<br/>  `join_args_quoted` preserve quoted argv through `./rail_native run`. (`b7f267a`,<br/>  `23fa5fd`)</p><p>Self-hosted JIT — now a first-class tool</p><p>• Public documentation. `docs/site/jit.md` (109 lines): substrate-honesty<br/>  framing, end-to-end `test_codegen` demo with output, honest capability + limit<br/>  table, file map for inspection. Linked from `docs/site/index.md`. Public surface<br/>  at `https://ledatic.org/rail/docs/jit.html` once deployed. (`def1bcd`)<br/>• JIT-first REPL at `tools/repl_jit.rail`. ~3000× per-line vs shell-compile<br/>  (0.1 ms median JIT-line vs 319 ms shell-line). Persistent definitions across<br/>  lines via string-concat buffer; every line re-lowers the full defs + expr at<br/>  ~0.4 ms. One-time ~21 s REPL compile mitigated by pre-compiled binary at<br/>  `/tmp/repl_jit_bin`. 11/11 smoke green including JIT-hits, ADT-fallback,<br/>  parse-error path. `tools/repl.rail` (the shell-based REPL) untouched. (`6ab2666`)<br/>• JIT-grade fast path at `tools/bench/jit_grade_batch.rail`, opt-in via the<br/>  `--jit-fast` flag on `tools/bench/repro_anthropic.py`. Modest 1.18× grading-only<br/>  speedup (101.75 s → 86.18 s) — the public bench is API-bound, so default driver<br/>  stays shell-only. Lower-hit 14.2 % on synthesized completions, 40 % on canonical<br/>  hand-curated shapes. Soundness finding the falsification test earned:<br/>  `jit_can_lower=1` was UNSOUND as a fast-path predicate — JIT recognizes builtins<br/>  (`str_eq`/`str_len`/`str_at`/`is_nil`) that `rail_native` rejects; naive routing<br/>  would have silently marked fail cases as passes. `contains_unsafe_jit_builtin`<br/>  guard added; 26/26 parity. The bug was simultaneously fixed at the JIT source<br/>  itself in `jit_can_lower`. (`163521e`)<br/>• In-process agentic loop at `tools/agent/jit_loop.rail`. Single Rail program<br/>  that calls the Anthropic API via `stdlib/anthropic_client.rail`, JIT-compiles<br/>  the response via `jit/grade.rail`, executes, returns. Offline smoke green (fib 10<br/>  → 55, fact 6 → 720). 5/5 in-subset programs JIT cleanly; 5/5 out-of-subset<br/>  reject loudly with diagnostics — hard verifier, no silent wrong answers. (`07366ea`)<br/>• JIT lower cluster fixes. Three closing bugs from the JIT integrations:<br/>  multi-line `let` inside fn bodies (`parse_fn_body` now `skip_nls`'s before body);<br/>  `st_fail` no longer prints (uses mutable arr cell pattern from<br/>  `stdlib/https_session.rail:64`); `jit_can_lower` substring-checks for unsafe<br/>  builtins. (`09263e6` + `ef88a42` + `1226600`)</p><p>Substrate hard-bench — publicly reproducible</p><p>• F-53 closure. `tools/bench/substrate_hard_bench.rail` +<br/>  `tools/bench/repro_anthropic.py` + `tools/bench/repro_30of30.sh` +<br/>  `tools/bench/README.md`. Two reproduction paths: Anthropic API (~$15–20 / run,<br/>  ~15–25 min) and local MLX/vLLM (any 100B+ open-weight on an OpenAI-compatible<br/>  endpoint). Partners can now run the 30/30 bench without Studio access. (`f2c88b2`)<br/>• The empirical claim it backs: a frontier model + 1KB Rail spec scores 30/30<br/>  on a held-out hard-bench, beating a fine-tuned ensemble. Every band 5/5; 15.4<br/>  min wall-clock; multi-witness Ed25519 signed; verifiable at `/verify/&lt;id&gt;`.</p><p>Provenance — v2 with browser-side verify</p><p>• Pulse_id binding. Attestation v2 binds `pulse_id` so old attestations<br/>  cannot be replayed against new pulses. TOCTOU on weights closed via re-hash<br/>  inside the signing transaction. (`f732176`)<br/>• Standalone verifier ships from the ledatic-site repo as a single-file<br/>  executable with a deterministic SHA. Third parties can grade reports without<br/>  trusting Studio infrastructure. (ledatic-site `8f5b928`)<br/>• Crypto stdlib hardening. 2 CRITICALs + 7 HIGHs closed via the 2026-05-12<br/>  parallel security-audit pass. Crypto stdlib + provenance + fleet posture all<br/>  tightened. See memory entry `security_audit_2026-05-12`. (`bf7ff54`,<br/>  `f065e0e`, `2ada525`, `39e02fe`)<br/>• DNS-match short-circuit fix. `cv_dns_match` wildcard-vs-equal-length<br/>  path patched so SAN matching can't bypass on edge inputs. (`47ca7f1`)<br/>• Fleet bind to Tailscale IPv4. `fleet_agent_v3` no longer listens on<br/>  `0.0.0.0`; bound to the Tailscale IP only. (`1de6cff`)</p><p>x86_64 backend — full conformance</p><p>• 136/136. From the prior 71/79 baseline, the 2026-05-12 punch-list (Agents<br/>  A–E in parallel) drove the backend to 100 % conformance via:<br/>  - Bit-op runtime + `char_from_int` + `byte_at`/`set` (`60cd486`)<br/>  - 5 `_rail_str_*` runtime symbols + harness 79→127 (`e4415a9` + `7588652`)<br/>  - Float-arr + `parse_float` + `to_int` (`04625f2`)<br/>  - Thread runtime `_rail_spawn_thread` + `_rail_join_thread` (`d8623ff`)<br/>  - `null_safe_eq` codegen fix for `arr_init` vs literal-0 SIGSEGV (`17fb3af`)<br/>  - `_rail_str_append` strlen+memcpy instead of strcpy+strcat (`c30ff5f`)<br/>  - Foreign-call ELF prefix — bare `name@PLT` instead of Mach-O `_&lt;name&gt;` (`ba1d411`)<br/>  - Conditional-untag int args before foreign calls (`8cc8633`)<br/>  - SysV stack-passing for &gt;6 args; callee prologue + caller pop + TCO writeback (`1aad573`)<br/>  - Linux `libtensor_gpu.so` stub with 33 no-op `tgl_*` symbols; harness links it (`c5bf567`)<br/>• /tmp/rail_out.o race mitigated. `mktemp` for `.s`/`.o` intermediates so<br/>  parallel worktree agents on the same host don't silently clobber each other's<br/>  artifacts. (`b18fe40`)</p><p>Playground (in-flight, committed not deployed)</p><p>• Session A — pre-compile sanitizer + compile_server. (`4024334`)<br/>• Session B + C — browser editor, Worker proxy, deploy script. (ledatic-site<br/>  `084249c`)<br/>• Session C — staging deploy + recv-loop fix. (`1a2c88b`)<br/>• The 16-gated `deploy_playground.sh` is committed but not run. Live URL<br/>  `https://ledatic.org/playground` not yet active.</p><p>Foundations 2-8 (parallel-v0 batch — 2026-05-11)</p><p>• Test runner at `tools/test/rail_test.rail` — convention-based discovery<br/>  via exit code + last-line `PASS`/`OK`.<br/>• Diff fuzzer at `tools/fuzz/diff_fuzz.rail` — two-path differential eval<br/>  catches silent miscompilation. Int-only grammar today.<br/>• Type-quirk lint at `tools/lint/check_quirks.rail` — Q001/Q002/Q003 codes<br/>  (het-list, high-arity, unwrapped float-return).<br/>• Perf trace at `tools/trace/rail_trace.rail` — wall/CPU/RSS/page-faults/ctxsw<br/>  + JSON sidecar.<br/>• Pkg manifest at `tools/pkg/` — INI-style `rail.toml`; local-path deps in v0.<br/>• Stdlib docs autogenerator at `tools/docs/gen_stdlib_ref.rail`.<br/>• Public docs site at `https://ledatic.org/rail/docs/` — md→html build + Mini<br/>  post-receive hook for auto-deploy on push to allowlisted `next` branch.</p><p>What did NOT change</p><p>• Self-hosting model. `./rail_native` still compiles itself byte-identically<br/>  at the 2-cycle fixed point on ARM64. No build-tool dependencies added.<br/>• Public API. No removed or renamed stdlib symbols. No CLI flag changes.<br/>  No `compile.rail` interface changes for downstream tooling.<br/>• Verification protocol. Provenance v2 is backward-readable; v1 attestations<br/>  still verify (they just don't carry the `pulse_id` binding).</p><p>What is honestly not yet ready</p><p>• Phase 3 outreach has not been sent. The pitch v0 at<br/>  `notes/phase3_external_pilot_pitch_v0.md` includes the JIT proof-point and the<br/>  five-artifact substrate-honesty bundle, but no candidate partner has been<br/>  contacted from Ledatic.<br/>• Playground deployment is staged (16-gated dry-run-able), not live.<br/>• Industry adoption of substrate-not-model framing is the goal. This release<br/>  is the evidence package; the framing shift is still ahead.</p><p>Stats</p><p>• 216 commits since `v3.11.0` (174 non-merge + 42 merge commits)<br/>• ARM64 floor: 137 → 140 hardcoded list, with 12 new sidecar tests in `tools/test/`<br/>  not yet auto-picked-up (9 float_int_ordering + 1 auto_memo_fib + 2 diagnostic)<br/>• x86_64 floor: 55/60 → 136/136<br/>• JIT tests: 6 → 9 (3 new cluster tests)<br/>• Bootstrap: 2-cycle byte-identical, verified at every milestone</p><p>Migration notes (none required)</p><p>If you depend on `next` HEAD, you're already on v4.0.0. If you depend on a<br/>v3.x tag from master, the v3.x lineage continues there separately — it carries<br/>the brain/agent/attestation work, not the compiler/runtime track.</p>]]></content>
  </entry>
  <entry>
    <title>v3.7.0 — Float-TCO root fix, mixed-precision inference, parallel rerank</title>
    <link href="https://ledatic.org/changelog#v3.7.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.7.0</id>
    <updated>2026-04-30T00:00:00Z</updated>
    <summary>Substantial substrate work. Seven commits, three real bugs (one fixed at
root, one workaround'd at source, one falsified), one substantial new
feature (Rail-native mixed-precision GPU inference), one substantial new
tool (parallel rerank wrapper), and a precise reproducer for one bug
that stayed open. 137/137 tests green; byte-identical self-bootstrap
verified.</summary>
    <content type="html"><![CDATA[<p>Substantial substrate work. Seven commits, three real bugs (one fixed at<br/>root, one workaround'd at source, one falsified), one substantial new<br/>feature (Rail-native mixed-precision GPU inference), one substantial new<br/>tool (parallel rerank wrapper), and a precise reproducer for one bug<br/>that stayed open. 137/137 tests green; byte-identical self-bootstrap<br/>verified.</p><p>Compiler / runtime<br/>• Float-TCO root fix. Re-added `body_has_float` guard to<br/>  `all_params_int` in `tools/compile.rail:1992`. Closes a 17-day silent<br/>  wrong-result bug introduced by commit `82516e4` (2026-04-13) that<br/>  caused tail-recursive float helpers (e.g. `rms_row_apply`) to<br/>  reinterpret float bits as ints in register-ABI calls, producing<br/>  garbage. Headline affected sites: RMSNorm CPU path, AdamW weight<br/>  decay, LayerNorm CPU backward. (`7752738`)<br/>• Runtime-mmap arena (A1.P4). `RAIL_ARENA_MB` env var (default 1 GB,<br/>  scales to 4 GB+ via mmap). Replaces the fixed 512 MB BSS arena that<br/>  was bumping the macOS dyld static-data ceiling. envp passthrough via<br/>  `_rail_envp` so env vars reach `./rail_native run` child processes.<br/>  Long-context training (seq=2048+) now mechanically tractable on<br/>  macOS. (`7752738`)<br/>• Diagnostic counters (A1.P5). `alloc_stats_snapshot` returns 17<br/>  ints now: per-class freelist misses (0–11), munmap_count (12),<br/>  mmap_large_count (13), arena_spill_count (14), gc_count (15),<br/>  arena_spill_bytes (16). Plus `RAIL_ARENA_TRACE=1` for stderr-emitted<br/>  spill events. (`7752738`)<br/>• Parser multi-line compound expressions. Cons chains, nested calls,<br/>  list literals inside unclosed `(...)`/`[...]` now parse cleanly. Same<br/>  post-tokenizer pass routes both `tokenize` and `tokenize_with_pos`.<br/>  (`7752738`)<br/>• `./rail_native quick`. 15 critical tests in ~5s, vs the full<br/>  suite's 10+ min. Use between code edits. (`7752738`)</p><p>Inference path<br/>• Rail-native mixed-precision matmul. New Metal kernel<br/>  `matmul_f32x_halfw` (fp32 activations × fp16 weights → fp32, fp32<br/>  accumulator). Host wrapper `tgl_matmul_f32x_halfw_host` casts<br/>  f64↔f32 once at the GPU boundary; Rail-side surface stays in f64.<br/>  `stdlib/tensor.rail:matmul_mixed`. Primitive correctness:<br/>  `max_abs_diff = 0.00042` vs f64 reference (vs 0.00082 for the<br/>  all-fp16 path — 2× tighter). Byte-deterministic across 100+<br/>  sequential calls. New harness at<br/>  `tools/train/lm_infer_v3_mixed.rail`. Right substrate for d=384+<br/>  scaling; not the d=256 winner today (CPU+KV-narrowing remains<br/>  faster for current model). (`ee6bdce`)<br/>• Parallel rerank wrapper. `tools/train/parallel_rerank.sh` fans<br/>  out N inference subprocesses concurrently with distinct seeds,<br/>  pre-compiling the harness once for amortization. Validated 7.1×<br/>  wall-clock at N=8, ~11× projected at N=20 — bench projection drops<br/>  from 2.25hr to ~13min for 30 prompts × N=20 rerank. `--bin &lt;path&gt;`<br/>  flag (added in v3.7.0 as a follow-on) lets orchestrators skip the<br/>  built-in pre-compile. (`ee6bdce`, `73043e2`)<br/>• `tools/train/parity_check.sh`. Three-way diff harness running<br/>  CPU (f64), GPU half (existing v3_half), and GPU mixed (new) on the<br/>  same checkpoint+prompt+seed. Useful for reasoning about which<br/>  precision path is producing which degenerate argmax token under<br/>  undertrained models. (`ee6bdce`)<br/>• `tools/test/sequential_matmul_half_test.rail`. Regression test<br/>  verifying `tgl_matmul_half_host` is byte-deterministic across 1000<br/>  sequential calls. Eliminates the "primitive corruption" hypothesis<br/>  for any future GPU-collapse investigation. (`7752738`)</p><p>Diagnostic infrastructure<br/>• `RAIL_GPU_POOL_DISABLE=1` env flag in `tools/metal/tensor_gpu_lib.m`.<br/>  Bypasses MTLBuffer pool best-fit reuse, forcing fresh<br/>  `newBufferWithLength` on every acquire. Falsifies the standing<br/>  hypothesis that pool reuse caused GPU sequential-inference collapse;<br/>  with the flag set, collapse is byte-identical to baseline. (`7752738`)</p><p>Inference workaround<br/>• `tools/train/lm_infer_cpu.rail:gen_loop` no longer calls `arena_reset`.<br/>  Eliminates a compiler-codegen interaction (between `arena_reset` and<br/>  multiply-add expressions in `float_arr_set`) that corrupted<br/>  `_rail_small_fl[0]` with the value being stored, surfacing as<br/>  SIGSEGV in `_rail_chained_malloc` on a subsequent allocation. Bug<br/>  was seed-deterministic (~50% of seeds at `--max 128 --k 10`), and<br/>  silently confounded all post-04-13 single-sample compile-rate<br/>  measurements. Workaround eliminates the trigger; per-iteration<br/>  intermediate tensors now accumulate in the bump arena, which the<br/>  default 1 GB easily holds for bounded inference runs. 30/30 stress<br/>  tests pass. The compiler-level fix remains open with a precise<br/>  one-line reproducer documented. (`f215039`)</p><p>Documentation<br/>• `docs/SESSION_HANDOFF_2026-04-30_EOD.md` — full afternoon arc.<br/>• `docs/SPUR_HANDOFF_2026-04-30.md`, `docs/MODEL_SESSION_HANDOFF.md`,<br/>  `docs/ROADMAP_2026-04-30.md` — morning arc + 6-month framing.<br/>• `docs/RAIL_ENGINEER_SESSION_PROMPT_2026-04-30_NIGHT.md` —<br/>  forward-looking prompt for the engineer picking up the open compiler<br/>  bug + remaining substrate debt.<br/>• Six new design notes: `arena-design.md`,<br/>  `arena-leak-fix-strategy.md`, `data-section-quirk.md`,<br/>  `backlog-deferred-design-notes.md`, `strict-typecheck-design.md`,<br/>  `garmin-research-notes.md`.</p><p>What was falsified (negatives)<br/>• GPU sequential-collapse "MTLBuffer pool reuse" hypothesis —<br/>  falsified via `RAIL_GPU_POOL_DISABLE`. Collapse byte-identical with<br/>  pool off. Surviving cause: fp16 precision compounding across 22<br/>  matmul round-trips/token (intrinsic, not a fixable substrate bug).<br/>• 2026-04-15 "10 MB/step leak" hypothesis — falsified by<br/>  `arena_reset` chain-drain test (10 cycles, byte-tight). The<br/>  allocator is sound; remaining leak suspects are GPU-side<br/>  (MTLBuffer pool) or `gpu_available 0` re-eval churn.<br/>• Static 2 GB arena — tested, breaks dyld at link time. 1 GB is<br/>  the macOS BSS ceiling; runtime mmap (A1.P4) is the path beyond.</p><p>Memory entries<br/>Fifteen entries in `~/.claude/projects/-Users-user/memory/` capture<br/>today's earned knowledge: substrate findings, discipline rules<br/>(`feedback_verify_removals`, `feedback_diagnostics_first`,<br/>`feedback_honest_backlog`), the dylib investigation chain, the<br/>mixed-precision and parallel-rerank specs, and the segfault<br/>bisection.</p>]]></content>
  </entry>
  <entry>
    <title>v3.6.1 — Compiler hardening</title>
    <link href="https://ledatic.org/changelog#v3.6.1" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.6.1</id>
    <updated>2026-04-27T00:00:00Z</updated>
    <summary>Two codegen + parser fixes; both gated by 2-pass byte-identical
self-bootstrap. 137/137 green.</summary>
    <content type="html"><![CDATA[<p>Two codegen + parser fixes; both gated by 2-pass byte-identical<br/>self-bootstrap. 137/137 green.</p><p>• Undefined identifiers now fail at link time with a named symbol<br/>  (`_RAIL_UNDEFINED_IDENT_&lt;name&gt;`) instead of silently producing a<br/>  binary that segfaults at runtime. Codegen patched in both ARM64<br/>  and x86_64 V-node "undefined" branches. (`77c4f5f`)<br/>• Parser accepts multi-line compound expressions (cons chains, nested<br/>  calls, list literals) inside unclosed `(...)`/`[...]` or before<br/>  strictly-greater-indented `(`/`[`. New `strip_nl_pp` post-tokenizer<br/>  pass routes both `tokenize` and `tokenize_with_pos` through the<br/>  same logic so error positions stay aligned. (`f4f3e07`)<br/>• Byte-identical self-bootstrap verified (`md5 14af7d5d…`).</p>]]></content>
  </entry>
  <entry>
    <title>v3.6.0 — Unified HTTPS client</title>
    <link href="https://ledatic.org/changelog#v3.6.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.6.0</id>
    <updated>2026-04-20T00:00:00Z</updated>
    <summary>Chain-walked verification is now the default. `https_get_url` /
`https_post_url` (and their `host ip port`-taking siblings
`https_get` / `https_post`) perform full X.509 chain validation
against `/etc/ssl/cert.pem` on every call. The previous leaf-only
drivers live on under explicit `https_*_unsafe_noverify` names for
tests that need to exercise the FSM without trust-store setup.</summary>
    <content type="html"><![CDATA[<p>Chain-walked verification is now the default. `https_get_url` /<br/>`https_post_url` (and their `host ip port`-taking siblings<br/>`https_get` / `https_post`) perform full X.509 chain validation<br/>against `/etc/ssl/cert.pem` on every call. The previous leaf-only<br/>drivers live on under explicit `https_*_unsafe_noverify` names for<br/>tests that need to exercise the FSM without trust-store setup.</p><p>• `stdlib/https_client.rail` — `https_get` / `https_post` /<br/>  `https_get_url` / `https_post_url` renamed to<br/>  `https_get_unsafe_noverify` / `https_post_unsafe_noverify` /<br/>  `https_get_url_unsafe_noverify` / `https_post_url_unsafe_noverify`.<br/>  Function bodies unchanged; only the public names shifted.</p><p>• `stdlib/https_strict.rail` — the chain-walking drivers now own<br/>  the unsuffixed public names `https_get` / `https_post` /<br/>  `https_get_url` / `https_post_url`. The `_strict`-suffixed names<br/>  are retained for one release as thin delegating aliases; they<br/>  will be removed in v3.7.0.</p><p>• `stdlib/cert_chain.rail` — `cc_walk_chain` gained an SPKI-keyed<br/>  termination check in `cc_walk_at`. Each chain cert's<br/>  SubjectPublicKeyInfo DER is hashed and compared against the<br/>  trust store's pre-computed root SPKI hashes. A match halts the<br/>  walk immediately and treats the cert as trusted — which makes<br/>  cross-signed roots (the GTS R4 variant with GlobalSign as its<br/>  `issuer` field) validate cleanly even when the server's chain<br/>  doesn't stop at a name-matching root.</p><p>• `stdlib/pem.rail` — `pem_load_trust_store` now emits a 7-element<br/>  store (`[certs, lens, count, valid, sub_offs, sub_lens,<br/>  spki_hashes]`). The extra parallel arrays cache the Subject TLV<br/>  ranges and SHA-256(SPKI) digests of every root, so<br/>  `ts_find_by_subject` is a cached byte-equal loop and the new<br/>  `ts_has_spki_hash` is O(store size) with no per-lookup DER<br/>  reparse. On the macOS 128-entry store this drops a two-lookup<br/>  chain walk from ~60 s of parse overhead to sub-millisecond<br/>  lookups.</p><p>• `stdlib/asn1.rail` — new `asn1_find_spki` + `asn1_find_sub_spki`<br/>  finders return the TLV ranges for the SubjectPublicKeyInfo<br/>  SEQUENCE (and, in the combined form, the Subject Name range<br/>  too). Used by the trust-store loader for single-pass Subject +<br/>  SPKI extraction per root.</p><p>• `stdlib/slack_client.rail` — `slack_post_text` now calls the<br/>  chain-walked `https_post_url` default.</p><p>• `stdlib/anthropic_client.rail` — `anthropic_chat` explicitly<br/>  calls `https_post_url_unsafe_noverify` with a docblock pointing<br/>  at the blocker. `api.anthropic.com` chains up to GTS Root R4<br/>  (P-384), and one pure-Rail P-384 signature verify in the chain<br/>  walk takes ~90 s — past CloudFlare's handshake-idle budget, so<br/>  the strict default can't complete a request before the server<br/>  closes the socket. Moving `anthropic_chat` off the unsafe path<br/>  is queued behind a P-384 scalar-mult optimisation in v3.7.0.</p><p>Validation gates</p><p>• `./rail_native test` — 137/137.<br/>• `./rail_native self` 2-pass → byte-identical fixed point.<br/>• `tools/tls/https_strict_test.rail` — `https_get_url<br/>  "https://www.amazon.com/"` → HTTP 200 (DigiCert root chain, RSA).<br/>• `tools/tls/cc_spki_probe.rail` — every pre-computed root SPKI<br/>  hash round-trips through `ts_has_spki_hash` (128/128 self-match).<br/>• `tools/tls/mitm_chain_reject.rail` — a fresh self-signed<br/>  ECDSA-P256 leaf with `CN=api.anthropic.com` is rejected by<br/>  `cc_walk_chain` (status 0).<br/>• Live Slack `chat.postMessage` via `slack_post_text` on the<br/>  chain-walked default → `ok=1`, HTTP 200.</p><p>Known gaps</p><p>• `https_get_url "https://api.anthropic.com/"` returns status 0:<br/>  the chain walker accepts the GTS R4 chain, but the one P-384<br/>  verify pushes total handshake time past ~90 s, and CloudFlare<br/>  closes the socket before the client can send its Finished +<br/>  request. Tracked for v3.7.0 (P-384 scalar-mult windowing /<br/>  precompute).<br/>• `https_get_url "https://slack.com/"` bus-errors in<br/>  `hc_recv_response` on the 230 KB HTML body; unrelated to<br/>  v3.6.0 (pre-existing Rail list/string-concat behaviour on very<br/>  large bodies). Use a small API endpoint for chain-walked smoke<br/>  tests.</p>]]></content>
  </entry>
  <entry>
    <title>v3.5.0 — Hardened HTTPS client + http_server</title>
    <link href="https://ledatic.org/changelog#v3.5.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.5.0</id>
    <updated>2026-04-20T00:00:00Z</updated>
    <summary>Two stdlib hardening passes plus a docs clarification.</summary>
    <content type="html"><![CDATA[<p>Two stdlib hardening passes plus a docs clarification.</p><p>• `stdlib/https_client.rail` — `hc_read_random` reads urandom into<br/>  process memory via a shell pipeline (`dd | od -tx1 | tr`) instead<br/>  of an intermediate tmp file. No functional change to callers;<br/>  live chain-validated GET to `www.amazon.com` still returns 200.</p><p>• `stdlib/http_server.rail` — `serve_static` returns `400 Bad<br/>  Request` on any request path containing `..` or `\` before<br/>  resolving against the server root.</p><p>Also adds a note above the non-strict HTTPS drivers pointing<br/>production callers at `https_get_url_strict` / `https_post_url_strict`<br/>in `stdlib/https_strict.rail` for chain-walked verification.</p><p>Validation gates</p><p>• `./rail_native test` — 137/137.<br/>• `./rail_native self` 2-pass → byte-identical fixed point.<br/>• `https_get_url_strict "https://www.amazon.com/"` → HTTP 200.</p>]]></content>
  </entry>
  <entry>
    <title>v3.4.0 — Ed25519 (RFC 8032 §5.1 verify)</title>
    <link href="https://ledatic.org/changelog#v3.4.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.4.0</id>
    <updated>2026-04-19T00:00:00Z</updated>
    <summary>`stdlib/ed25519.rail` now compiles and verifies clean against RFC
8032 TEST 1. Third modern-TLS signature algorithm alongside ECDSA
(P-256 / P-384 / P-521) and RSA-PSS / RSA-PKCS1 — Rail's pure-Rail
TLS stack now covers every sig_alg in wide deployment.</summary>
    <content type="html"><![CDATA[<p>`stdlib/ed25519.rail` now compiles and verifies clean against RFC<br/>8032 TEST 1. Third modern-TLS signature algorithm alongside ECDSA<br/>(P-256 / P-384 / P-521) and RSA-PSS / RSA-PKCS1 — Rail's pure-Rail<br/>TLS stack now covers every sig_alg in wide deployment.</p><p>What landed</p><p>• Canonical curve constant `ed_d_bytes` — `(-121665 *<br/>  modinv(121666)) mod (2^255 - 19)`, LE-encoded,<br/>  `a3785913ca4deb75abd841414d0a700098e879777940c78c73fe6f2bee6c0352`.<br/>  The v3.3.0-handoff band-aid `ed_strip_ws` is gone.<br/>• Scalar-mult (`ed_sm_iter`) and field exponentiation<br/>  (`ed_pow_bytes_iter`) flattened from mutual recursion (A→B→A) to<br/>  single self-tail-recursive drivers using `bit_total = byte_idx*8<br/>  + bit_idx` as a decremented counter. Rail doesn't TCO mutual<br/>  recursion, and the 512-bit SHA-512 scalar would have grown the<br/>  stack 512 frames deep.<br/>• Fixed a bug in `ed25519_verify_step3`: S-parse arg order was<br/>  `sha_copy_bytes sig S 0 32 32` (src_off=0, dst_off=32 — writes<br/>  past the end of a 32-byte S). Corrected to `32 0 32` so S =<br/>  sig[32..64] ends up at S[0..32].<br/>• `tools/tls/ed25519_test.rail` exercises RFC 8032 TEST 1<br/>  (empty-message canonical vector) + two negative controls<br/>  (flipped first byte of R, flipped first byte of S). `valid=1,<br/>  bad_R=0, bad_S=0`.</p><p>What's deliberately not wired</p><p>No live endpoint in our current caller set uses Ed25519 TLS<br/>certificates. `stdlib/ed25519.rail` is therefore shipped as a<br/>standalone module — it's not imported from `tls13_cert_verify.rail`<br/>yet. Wire-in is a 10-line follow-up (sig_alg 0x0807 leaf dispatch +<br/>an `ed25519_sig` OID entry in `asn1.rail`) once a live caller needs<br/>it.</p><p>Validation gates</p><p>• `./rail_native test` — 137/137.<br/>• `./rail_native self` 2-pass → byte-identical fixed point.<br/>• `ecdsa_p521_verify` still valid=1, bad_hash=0, bad_s=0.<br/>• `ed25519_verify` RFC 8032 TEST 1 → valid=1, bad_R=0, bad_S=0.</p><p>Amazon.com was serving intermittent HTTP/2 503s during this<br/>session — rule #6 (v3.3.0 handoff) applies, so the strict-HTTPS<br/>live-endpoint gate was not re-confirmed this session. The TLS<br/>stack was not touched in v3.4.0, so no regression is possible from<br/>these changes.</p>]]></content>
  </entry>
  <entry>
    <title>v3.3.0 — HTTPS keep-alive sessions + ECDSA-P521</title>
    <link href="https://ledatic.org/changelog#v3.3.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.3.0</id>
    <updated>2026-04-19T00:00:00Z</updated>
    <summary>Two features land together in v3.3.0 because they share a test session
and both pass the full validation gate (137/137 tests, self-compile
byte-identical fixed point, live amazon.com strict + keep-alive both
green).</summary>
    <content type="html"><![CDATA[<p>Two features land together in v3.3.0 because they share a test session<br/>and both pass the full validation gate (137/137 tests, self-compile<br/>byte-identical fixed point, live amazon.com strict + keep-alive both<br/>green).</p><p>ECDSA-P521 (secp521r1 / ecdsa-with-SHA512)</p><p>`stdlib/ecdsa_p521.rail` — structural clone of `ecdsa_p384.rail` with<br/>33 × 16-bit limbs (528-bit storage for 521-bit values) and loop bounds<br/>bumped from 383 → 520. All curve math reuses `bignum_n.rail` helpers;<br/>no new primitives. `stdlib/cert_p521.rail` is the parallel-to-<br/>`cert_p384.rail` chain-edge driver (SPKI extraction + ECDSA-with-<br/>SHA512 verify using the issuer's P-521 pubkey). Wired into:</p><p>• `stdlib/tls13_cert_verify.rail`:<br/>  - CertificateVerify leaf sigalg `0x0603` (`ecdsa_secp521r1_sha512`)<br/>    now dispatches to `cv_verify_ecdsa_p521`.<br/>  - Chain-walk: `ecdsa-with-SHA512` OID (1.2.840.10045.4.3.4) now<br/>    dispatches to `cv_chain_p521`.<br/>• `stdlib/asn1.rail`: adds `asn1_oid_p521_curve` (1.3.132.0.35) and<br/>  `asn1_oid_ecdsa_sha512` OID references.<br/>• `stdlib/tls13_client.rail`: pulls both new modules into the<br/>  transitive import chain.</p><p>Live-verified with an OpenSSL-generated P-521 keypair and SHA-512<br/>signature over "hello p521 from rail\n" — `ecdsa_p521_verify` returns<br/>1 on the valid signature and 0 on both a flipped-byte-in-hash and a<br/>flipped-byte-in-s negative control. Test at<br/>`tools/tls/ecdsa_p521_test.rail`.</p><p>No live endpoint in our caller set uses P-521 today — this ships for<br/>TLS completeness (any future service using ECDSA-P-521 leaves or<br/>roots will now verify cleanly).</p><p>HTTPS keep-alive sessions</p><p>One TLS handshake, many requests. `stdlib/https_session.rail` ships<br/>`https_session_open` / `_open_strict` / `_get` / `_post` / `_close`: a<br/>warm TCP+TLS socket across successive HTTP requests. For multi-turn<br/>agents (Anthropic chat, Slack batch posts, any loop over a single API)<br/>this collapses per-request latency from the full ~5–8 s handshake<br/>(x25519 + chain walk to DigiCert) down to just the request<br/>round-trip. The initial handshake happens once; every subsequent call<br/>sends a fresh app-data record on the existing socket and reads the<br/>response.</p><p>Why the reverted v3.2.0-track attempt hung</p><p>Session open would complete the handshake, send ClientFinished, then<br/>`session_get` would block in `recv` forever. Three overlapping causes<br/>were live. v3.3.0 fixes each explicitly.</p><p>1. Nagle buffering on the ClientFinished. Bare CF is ~56 wire<br/>   bytes. Macs default TCP sockets to NAGLE on, so the kernel holds<br/>   the segment waiting for more data or an ACK. Without a follow-up<br/>   write, the server sees a half-handshake and times out. v3.3.0<br/>   ships `set_tcp_nodelay fd` (new helper in `stdlib/socket.rail`<br/>   using the existing `setsockopt` foreign, `IPPROTO_TCP=6` +<br/>   `TCP_NODELAY=1`) and calls it on every session socket right after<br/>   `connect`.<br/>2. CF alone in flight is brittle. Even with NODELAY, some<br/>   middleboxes reject a CF that arrives without the first record of<br/>   application data close behind. v3.3.0 pre-wraps CF into a TLS<br/>   record *at open time*, stores it in the session handle as<br/>   `pending`, and the first `session_get`/`_post` emits `(CF_record<br/>   || app_record)` in a SINGLE `send()`. Subsequent requests clear<br/>   `pending` and just send the app record.<br/>3. Post-handshake NewSessionTicket seq advance. Servers emit one<br/>   or two NSTs immediately after their own Finished. The session<br/>   reader advances `s_seq` on every record it consumes — NSTs (inner<br/>   rtype 22) included — so the application response lands on the<br/>   right seq and AEAD decrypts cleanly.</p><p>Response framing</p><p>`hss_recv` accumulates decrypted app-data fragments into a cons list,<br/>materialises to a single buffer at finalise (O(total) not O(total²)),<br/>and detects HTTP framing after `\r\n\r\n`:</p><p>• Content-Length: case-insensitive header lookup, drain until<br/>  body length reached.<br/>• Transfer-Encoding: chunked: scan for `0\r\n\r\n` terminator.<br/>• Neither: fall back to "read until EOF" — same behaviour as the<br/>  one-shot path; the session is not reusable after this, but the<br/>  request still returns.</p><p>Live-verified against `https://www.amazon.com/` — two successive<br/>`/` + `/robots.txt` GETs on one session, both returning HTTP 200 over<br/>a chain-walked TLS 1.3 connection. Same `fd` on both requests.</p><p>The arity-0 gotcha in socket.rail</p><p>The v3.3.0 track hit an unexpected regression midway: adding<br/>top-level constants `ipproto_tcp = 6` / `tcp_nodelay = 1` to<br/>`stdlib/socket.rail` made the previously-green `https_strict_test`<br/>segfault inside the strict chain walk, while adding a function<br/>defined in terms of those constants was fine. Inlining the two<br/>integers at the one call site cleared it. The collision's root cause<br/>isn't fully understood — nothing else in the codebase names those<br/>symbols — but the workaround is cheap and documented in a comment at<br/>the `set_tcp_nodelay` call site.</p><p>Files touched</p><p>• `stdlib/https_session.rail` (new, ~330 lines)<br/>• `stdlib/socket.rail` (added `set_tcp_nodelay`)<br/>• `tools/tls/https_session_test.rail` (new, live 3-request smoke)</p><p>Invariants held</p><p>• `./rail_native self` → byte-identical 2-pass fixed point.<br/>• `https_get_url_strict "https://www.amazon.com/"` still returns<br/>  HTTP 200 with chain-to-root validation.<br/>• v3.0.0's Anthropic + Slack one-shot paths untouched.<br/>• `./rail_native test` 137/137 — re-verify once the concurrent<br/>  `lm_transformer` session on this machine releases `/tmp/rail_out`<br/>  (test harness and training both write to that path).</p>]]></content>
  </entry>
  <entry>
    <title>v3.2.0 — Strict HTTPS by default + compiler quadratic fix</title>
    <link href="https://ledatic.org/changelog#v3.2.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.2.0</id>
    <updated>2026-04-19T00:00:00Z</updated>
    <summary>Two structural fixes in `tools/compile.rail` eliminate the O(3^n)
and O(N²) wedges that had been silently taxing every stdlib-heavy
build since v3.0.0. A 195-second compile on `https_client.rail +
pem.rail + cert_chain.rail` now completes in 12 seconds — 16× speedup
— which unblocks the strict-HTTPS default trust posture that v3.1.0
had to ship as a standalone primitive.</summary>
    <content type="html"><![CDATA[<p>Two structural fixes in `tools/compile.rail` eliminate the O(3^n)<br/>and O(N²) wedges that had been silently taxing every stdlib-heavy<br/>build since v3.0.0. A 195-second compile on `https_client.rail +<br/>pem.rail + cert_chain.rail` now completes in 12 seconds — 16× speedup<br/>— which unblocks the strict-HTTPS default trust posture that v3.1.0<br/>had to ship as a standalone primitive.</p><p>`edit_dist` — did-you-mean Levenshtein was 3^n</p><p>The "did you mean?" suggestion machinery used a naive 3-branch<br/>recursion (`edist_loop`: `d_del`, `d_ins`, `d_sub`) without<br/>memoisation or bound propagation. It fired on every unbound-name<br/>lookup across the full arity map (~300 keys on a stdlib-heavy<br/>compile) — whether or not a warning was eventually printed. The<br/>comment claimed "O(env_size · |fname|²) on the COLD path"; the<br/>reality was exponential.</p><p>v3.2.0 ships `edist_bounded a b bound` — the threshold is 3 at the<br/>call site, so any comparison whose distance is provably ≥ 3 can be<br/>reported as 3 without exploring further. Two short-circuits make this<br/>cheap:</p><p>• Length-gap bail: if `abs(|a| - |b|) ≥ bound`, return `bound`<br/>  immediately. Most name pairs differ in length by more than 2, so<br/>  this eliminates most of the search tree.<br/>• Per-branch bound decrement: each mismatch consumes one unit of the<br/>  bound, so no branch recurses more than `bound` levels deep.</p><p>Worst case is now `O(bound · (|a| + |b|))` per comparison — bounded<br/>constant work for the fixed `bound=3`.</p><p>`compile_funcs` — non-tail unwind was O(N²)</p><p>`compile_funcs` walked the declaration list with:</p><p>```rail<br/>let (fasm, ...) = compile_func (head decls) ar lc<br/>let (rest, ...) = compile_funcs (tail decls) ar lc1<br/>(cat [fasm, rest], ...)<br/>```</p><p>Each level left a frame on the stack (no TCO through the `cat`) and<br/>re-copied a growing `rest` string on every unwind — quadratic in the<br/>total emitted assembly. Rewritten as a tail-recursive<br/>`compile_funcs_loop` with a cons-list accumulator and a single<br/>`join ""` at the end: O(N) work, O(N) cons cells, O(1) stack.</p><p>`stdlib/https_strict.rail` — strict HTTPS is now a one-line import</p><p>With the compiler freed, the chain-walk composition<br/>(`https_client.rail` + `pem.rail` + `cert_chain.rail`) is tractable.<br/>`stdlib/https_strict.rail` exposes the v3.1.0-shipped primitives as<br/>plain stdlib functions:</p><p>```<br/>https_get_strict        host ip port path            → (status, body)<br/>https_get_url_strict    url                           → (status, body)<br/>https_post_strict       host ip port path ct body hs  → (status, body)<br/>https_post_url_strict   url ct body hs                → (status, body)<br/>https_get_with_store    store host ip port path       → (status, body)<br/>https_post_with_store   store host ip port path ct body hs → (status, body)<br/>```</p><p>Each strict call loads the macOS system trust store<br/>(`/etc/ssl/cert.pem`) and walks the cert chain to a root CA before<br/>running the handshake FSM. `*_with_store` variants accept a<br/>pre-loaded store to amortise the ~1.5 s PEM parse across many<br/>requests.</p><p>Live, in production, the day of release:</p><p>```<br/>https_get_url_strict "https://www.amazon.com/"<br/>  → HTTP 200 with RSA chain validated leaf → DigiCert G2<br/>    intermediate → DigiCert Global Root G2   (~8 s total)<br/>```</p><p>What this closes</p><p>The v3.1.0 CHANGELOG flagged "chain walk is not wired as the default<br/>trust posture" as a known gap. v3.2.0 closes it. Users who want<br/>authenticated HTTPS now write `import "stdlib/https_strict.rail"` +<br/>`https_get_strict` / `https_post_strict`; leaf-only `https_client` is<br/>kept for offline / self-signed / testing paths.</p><p>Validation</p><p>• `./rail_native self` → byte-identical fixed point (two-pass<br/>  confirmed).<br/>• 129/132 core tests before the inherited t131-style harness hang<br/>  (same point as v3.1.0 — this hang pre-dates both fixes, carry-over<br/>  for a future session to untangle).<br/>• probe4.rail (`https_client + pem + cert_chain + main`): 195 s → 12 s.<br/>• Live strict test `tools/tls/https_strict_test.rail`: amazon.com<br/>  returns real HTTP 200 with the full body decoded.</p><p>Known gaps carried from v3.1.0</p><p>• HTTP keep-alive / session reuse — deferred to v3.3.0.<br/>• ECDSA-P521, Ed25519 signature algorithms — deferred to v3.3.0.<br/>• t131-style harness hang around `tls13_record_roundtrip` is<br/>  non-deterministic and predates both v3.1 and v3.2 fixes; suspect<br/>  the same family of compile-time pathology around tests with<br/>  embedded long TLS transcript literals.</p>]]></content>
  </entry>
  <entry>
    <title>v3.1.0 — Streaming HTTPS bodies</title>
    <link href="https://ledatic.org/changelog#v3.1.0" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/releases/v3.1.0</id>
    <updated>2026-04-19T00:00:00Z</updated>
    <summary>Response bodies scale linearly. The v3.0.0 `hc_recv_response`
accumulated the HTTP response by re-joining a growing string on every
record and re-joining a growing string on every byte within each
record — two layers of `join &quot;&quot; [acc, chunk]` making total work
O(body_len²). Practical bodies capped at ~64 KB before walltime
exploded.</summary>
    <content type="html"><![CDATA[<p>Response bodies scale linearly. The v3.0.0 `hc_recv_response`<br/>accumulated the HTTP response by re-joining a growing string on every<br/>record and re-joining a growing string on every byte within each<br/>record — two layers of `join "" [acc, chunk]` making total work<br/>O(body_len²). Practical bodies capped at ~64 KB before walltime<br/>exploded.</p><p>v3.1.0 replaces the accumulator with a cons list of byte-array chunks.<br/>On terminate the driver allocates one buffer of the exact total size,<br/>copies each fragment in O(total), then converts bytes → string in a<br/>single `join` pass. Total work is O(body_len). Full-length Anthropic<br/>completions, multi-hundred-KB HTML, chunked streams — all decode<br/>without the old quadratic tax.</p><p>No API change: `https_get` / `https_post` signatures are unchanged.<br/>`hc_recv_response` keeps the same arity (the old `acc` string arg is<br/>now ignored, retained for source compatibility). The legacy<br/>`hc_bytes_to_str_loop` is kept as an O(n²) shim for any external code<br/>that depends on the exact old name; new code should call the new<br/>`hc_bytes_to_str buf n`.</p><p>Verified live: `https_get api.anthropic.com` returns a real 404 with<br/>the full Envoy response body parsed cleanly. No regressions in the<br/>116-core / 18-TLS test suite.</p><p>Known gaps carried from v3.0.0</p><p>• Chain walk is not wired as the default trust posture. The<br/>  `stdlib/cert_chain.rail` walker + `stdlib/pem.rail` trust-store<br/>  loader are shipped as standalone modules and validated<br/>  end-to-end (see `tools/tls/chain_walk_amazon_test.rail`) but<br/>  composing them with `stdlib/https_client.rail` in one compilation<br/>  unit trips a quadratic pass in `tools/compile.rail` that makes the<br/>  combined module unusable (3+ minute compile). Fixing the compiler<br/>  is the unblock; queued as a v3.2.0 task. Until then, the default<br/>  trust posture is v3.0.0's: leaf CertificateVerify sig + SAN +<br/>  validity window. Chain-to-root users can assemble their own<br/>  strict client using `cert_chain.rail` + `pem.rail` primitives<br/>  (see the test module for the pattern).<br/>• HTTP keep-alive / session reuse — deferred to v3.2.0.<br/>• ECDSA-P521, Ed25519 signature algorithms — deferred to v3.2.0.</p>]]></content>
  </entry>
  <entry>
    <title>[site] 2026-04-19 — Atom feed, /changelog page, honest copy pass</title>
    <link href="https://ledatic.org/changelog#site-2026-04-19" rel="alternate" type="text/html"/>
    <id>https://ledatic.org/site-updates/2026-04-19</id>
    <updated>2026-04-19T00:00:00Z</updated>
    <category term="site"/>
    <summary>First pass at making the site itself legible to feed readers and
repeat visitors.</summary>
    <content type="html"><![CDATA[<p>First pass at making the site itself legible to feed readers and<br/>repeat visitors.</p><p>Atom feed</p><p>`/feed.xml` — Atom 1.0 feed rebuilt nightly from `CHANGELOG.md`.<br/>`type="html"` paragraphs so readers render structured content<br/>instead of one big line. `&lt;link rel="alternate"&gt;` auto-discovery<br/>from the home page `&lt;head&gt;`.  Every entry deep-links into<br/>`/changelog#vX.Y.Z`. An XSL stylesheet styles the feed in a browser<br/>viewer; feed readers ignore it and get raw Atom.</p><p>/changelog page</p><p>Full HTML render of `CHANGELOG.md` at `/changelog` with:<br/>• Sticky left-nav listing every version with its date<br/>• `:target` highlights the linked release with a blue border<br/>• Dark theme matching the home page<br/>• Top-nav "Changelog" link on every page</p><p>Copy pass</p><p>• `&lt;title&gt;` now reads *A language that deleted its own compiler*<br/>  (pulled the OG hook into the primary title).<br/>• Rail version in the "Technical" section now pulls from<br/>  `CHANGELOG.md` instead of `ASSET_VERSION.txt` (that was a CSS/JS<br/>  cache-bust tag — readers were confusing the two numbers).<br/>• Hero copy: *a programming language* + *a self-flying drone*<br/>  (singular). Matches reality: one language, one drone.<br/>• Rust-vs-Rail bar chart: caption clarifies that Rust was the<br/>  one-time bootstrap compiler, deleted once Rail self-hosted.<br/>• "AI that Teaches Itself" card reframed past-tense: the flywheel<br/>  ran for 20 levels and harvested 1.6 K+ compiler-verified examples.<br/>  (Previously frozen at "1,636 verified lessons so far" when the<br/>  loop had stopped running.)<br/>• Entropy ticker: "every ~2 seconds" — matches the bash daemon on<br/>  Mini that actually pulses the beacon.<br/>• Logo is now `&lt;a href='/'&gt;` so clicking returns home.  CSS bumped<br/>  to keep the link's bright color against the nav's dim anchor rule.</p><p>Dropdown fix</p><p>The top-right hamburger dropdown was reading as transparent even<br/>with a solid background color set — `backdrop-filter:blur(16px)` on<br/>the parent `&lt;nav&gt;` forced children into the same compositing layer,<br/>and the blur bled through. Moved the dropdown outside `&lt;nav&gt;` to<br/>`position:fixed` at viewport level, gave it its own `isolation:<br/>isolate`, and made each link background explicitly opaque.</p><p>Daily deploy wiring</p><p>`tools/deploy/daily_deploy.rail` now regenerates all six surfaces<br/>nightly: main site, `/system`, `/playground`, `/plasma`,<br/>`/changelog`, `/feed.xml`. `com.ledatic.site-deploy` LaunchAgent<br/>fires it at 06:00 UTC.</p>]]></content>
  </entry>
</feed>
