Changelog
All notable changes to cheatah. This project is alpha — expect breaking changes between releases.
v1.11.7-alpha (2026-08-16) — the tables lay out, and the release path clears
Biome Standard 0.6.3-alpha — a PATCH: the only member that changes is this toolchain release. It repairs the documentation site's tables and finishes the accessibility pass against WCAG 2.2, removes a file-exfiltration primitive from the QA gate's browser, and unblocks the release path itself — which had been unable to converge. No language, library, compiler, or runtime surface changes shape; every program written against 0.6.2-alpha compiles and behaves identically.
Fixed — the documentation site's tables were not laid out as tables
The 2.1 pass gave .dtable display: block; overflow-x: auto so a wide table could not widen the page. The goal was right and the mechanism was wrong: display: block on a <table> takes it out of table layout, so the rows collapse into an anonymous shrink-to-fit box — width: 100% then sized only the frame while the cells hugged the left and the <th> stripe stopped short of the right edge. A table cannot be its own scroll container, so each one is now wrapped in a <div class="tscroll" tabindex="0"> that carries the frame and the overflow. Measured in a real engine: a 3000px table scrolls inside its wrapper while the document stays at 350px in a 360px viewport, so the property that motivated the original change still holds.
The same pass also left-aligned the sidebar and the on-this-page rail, on the grounds that centred nav is harder to scan. That is a readability opinion, not a criterion — SC 1.4.8 prohibits justified text, not centred, and is Level AAA. What it legitimately carried was SC 2.5.8 Target Size: a centred shrink-to-fit <li> is only as wide as its label. Alignment and target size are separable, so the <li> stays stretched and the label centres inside it. It also restored text-overflow on the rail, which the switch to flex had silently disabled — text-overflow has no effect on a flex container, so long headings had been hard-clipped with no ellipsis.
Fixed — four WCAG 2.2 AA gaps the 2.1 pass could not have known about
SC 2.4.7 Focus Visible. The permalink was
opacity: 0revealed on:hover, andopacityapplies to an element's whole rendering — its outline included. A keyboard user tabbing to a permalink got an invisible link and an invisible focus ring. Hover is not a mode of operation a keyboard has.SC 2.4.11 Focus Not Obscured (Minimum), new at AA in 2.2. Only member cards carried a
scroll-margin, so headings, overload rows and source lines could land entirely beneath the sticky header. Every[id]now clears it.SC 2.5.8. The permalink glyph (~10px) and the source link (~19px) sit in a flex row, not a sentence, so they never qualified for the criterion's inline exception. Both are 24px.
SC 1.4.12 Text Spacing. The header's fixed height clipped the brand under the spacing overrides the criterion requires a page to survive;
min-heightlets it grow.SC 1.3.1. Header cells emit
scope="col"(technique H63).
The gate grew eight checks over the stylesheet and the emitted pages, each verified to fail when its fix is reverted — a green check that cannot fail proves nothing.
The permalink half of that block is opt-out, via A11Y_ANCHORS=0. .member and .overload are Doxygen's per-member rows and .anchor is their permalink; a tree with no per-member headings has none of them, so demanding those selectors would mean writing dead CSS whose only purpose is to satisfy a counter — and because the opacity: 0 count is pinned at exactly 2, it would also forbid such a tree ever having a third hidden control for an unrelated reason. The knob has the same shape, and exists for the same reason, as the A11Y_LIGHT=0 that already excuses a tree shipping one theme on purpose. It defaults to on, so cheatah's own docs still enforce the pin across all 292 pages.
Security — the accessibility gate's browser is contained
The gate passed --allow-file-access-from-files so its harness could read the measured page's contentDocument across file:// origins. That flag relaxes the same-origin policy for the whole browser instance: any page it loads can read other local files and post them out. Our docs are our own, so the practical risk was low — but a gate that one day measures HTML from an untrusted branch would be a file-exfiltration primitive, and that is not a property to leave lying in a committed script. The flow is inverted instead: the measured page posts its own scrollWidth out with postMessage, which is cross-origin by design, and the flag is gone.
Browser invocation is now contained structurally rather than by convention (scripts/headless_browser.sh): a throwaway profile, and for Firefox --no-remote plus MOZ_NO_REMOTE=1, because firefox <url> hands the URL to an already-running instance and can ignore --headless entirely — during this work it opened a real tab in a developer's own browser.
Fixed — the coverage table oscillated, and no release could be pushed
The gate regenerates README's coverage table and fails if the file changed, telling you to commit and push again. But regions and branches jitter in the second decimal between otherwise identical runs — the systest lane's timing decides which defensive branches a run takes — and the values oscillated between two states (95.29 ↔ 95.30, 85.56 ↔ 85.58). So "commit and push again" produced the other value, and the next push demanded the first one back. The loop could not converge, and no release could be pushed at all. Regions and branches are advisory; Lines and Functions are what the gate enforces, and both stay exact and unrounded. Printing the advisory pair to one decimal makes the text stable — verified byte-identical across three consecutive regenerations.
Fixed —
The job failed on v1.11.4, v1.11.5 and v1.11.6 with env: 'build/release/bin/biome': No such file or directory, 14 milliseconds after its own build step logged Linking CXX executable bin/biome. run() cds into the clean-room temp directory before invoking biome, so a relative path stops resolving at the moment of use; the auto-detect branch already absolutized, the explicitly-passed argument did not — and a relative path is exactly what the workflow passes. Locally the argument is omitted, which is why the release step was green by hand and red in CI every single time.
The workflow is now workflow_dispatch only. It ran on release: [published] and mailed a failure on every release for a check that gates nothing, which trains everyone to ignore the one signal that is supposed to mean something. This hides no regression: the release's real acceptance test is the local scripts/test-standard-e2e.sh, which passes against the pushed tags. What must be true before the trigger goes back is recorded in the file — cheatah-gpu-linalg discovers its GPU toolchain from a developer workstation, so a consumer that fetched the members by tag through CPM, which is the exact flow the job exists to prove, cannot configure at all.
v1.11.6-alpha (2026-08-15) — the docs fit on a phone
Biome Standard 0.6.2-alpha — a PATCH: the only member that changes is this toolchain release. It fixes a tls client handshake bug and makes the documentation site accessible; no language, library, compiler, or runtime surface changes shape. Every program written against 0.6.1-alpha compiles and behaves identically — except one that previously could not connect to a server asking for a client certificate, which now connects.
Fixed —
A TLS 1.3 server that sends CertificateRequest (asking the client to authenticate) made the handshake fail. The client now declines it correctly — an empty Certificate message — and the connection completes.
Fixed — the documentation site was unusable on a phone
The site was 550px wide at a 320px viewport and scrolled sideways on every phone: a WCAG 2.1 SC 1.4.10 (Reflow) failure, on every page, that had shipped for months. The header grid declared a 248px sidebar track and a 224px TOC track at every breakpoint, so below 1000px — where the package switcher and the on-this-page rail are already hidden — their columns still forced a ~526px floor. Each breakpoint now drops the tracks whose content it hides, and wide content (code blocks, gtest Suite.Case chips, tables) is contained rather than allowed to set the page width. Phones get a capped, scrolling nav instead of ~40 links stacked above the first word of documentation.
Fixed — the rest of the WCAG 2.1 AA pass
--fg-faintmeasured 4.21:1 against--surface, under the 4.5:1 floor; it is now 4.80:1. Every foreground is checked against the lightest ground it is drawn on.Light mode was unreachable. The theme toggle was JavaScript, the serving CSP has no
script-src, and both generators bakeddata-theme="dark"into every page — so the stylesheet's light palette was dead code. The attribute is gone and aprefers-color-schemeblock honours the reader's OS setting.Touch targets reach 24px (44px for the phone sidebar), and the nav reads left-aligned.
Pages with no headings no longer emit an empty labelled "On this page" landmark.
Literal
**markers reached 54 pages, from two causes: Doxygen does not convert**bold**wrapping a`code`span, andmd_inlinepaired**inside each backtick-delimited segment independently, so bold spanning a code span never closed. Both fixed — the first in the sources, deliberately not by sweeping the emitted HTML, where2 ** 10andchar **argvare real source that a sweep would have corrupted.
Added — an accessibility gate, in cheatah
scripts/docs_a11y_gate.sh + scripts/a11y_check.purr, wired into scripts/qa_gate.sh. Reflow is measured in headless Chrome at 320/390/768/1280; contrast is computed from the stylesheet's own tokens; page structure (skip link, one <h1>, lang, no empty labelled landmark, no <script>, alt on every image) is checked across all 292 pages including the extension subsites.
--window-size=320 does not give a 320px viewport — Chrome clamps its window to 500px, so the obvious version of this gate silently measures 500px and passes while the phone is still broken. Pages are loaded in a fixed-width iframe instead. Verified in both directions: with the old stylesheet the gate reports 550px at a 320px viewport and fails; with the fix, 310px.
v1.11.5-alpha (2026-08-14) — the docs shed their scripts
Biome Standard 0.6.1-alpha — a PATCH: the only member that changes is this toolchain release, and the release changes no language, library, compiler, or runtime surface — the documentation site and its generator are the whole diff. Every program written against 0.6.0-alpha compiles and behaves identically.
Docs
The generated site is zero-JS by design: it renders fully under the strictest serving policy (
default-src 'none'; style-src 'self'; img-src 'self'). Both<script>tags are gone from every page,search-index.js(777 KB core + per-subsite copies) andcheatah-docs.jsare no longer emitted — 1.4 MB of scripts removed, 294 files, 14.6 MB total.What the scripts did is now done at generation time: the sidebar marks the current page (
class="active") and opens the<details>chain leading to it — per page, server-side. The theme toggle is gone (the site ships its dark theme); search is removed rather than shipped broken, since adefault-src 'none'host can never execute it.A page load is now ~51 KB over 3 requests (page + stylesheet + logo), down from ~815 KB with scripts that never ran.
The parity twin
docs/gen/generate.pycarries the same change; the generators' common output remains byte-identical.
Biome Standard 0.6.0-alpha — a MINOR: cheatah-gpu-linalg v0.4.4-alpha ADDS available()/unavailable_reason() to its purr module surface, the probe the design tells consumers to gate device work on (the acceptance test's program was the first purr consumer to ask). Additive only; every other member unchanged; this release carries the table.
v1.11.3-alpha (2026-08-14) — round three, one include
Biome Standard 0.5.3-alpha (a PATCH): the acceptance test's third round reached cheatah-gpu-linalg's cross-repo include and caught its sibling-checkout default (../cheatah-gpu), which does not exist inside a consumer's _deps tree. cheatah-gpu-linalg v0.4.3-alpha prefers the consumer's CPM source dir; every other member unchanged; this release only carries the table.
v1.11.2-alpha (2026-08-14) — the seam holds
The acceptance test's second round: with 0.5.1-alpha's fixes in, the fresh-directory e2e reached the consumer's COMPILE LINE and caught the next two seam defects. Biome Standard 0.5.2-alpha (still a PATCH — fixes only, no API change anywhere) carries the repaired members: cheatah-gpu v0.5.1-alpha and cheatah-gpu-linalg v0.4.2-alpha, with cheatah-plot and cheatah-space unchanged.
cheatah-gpu v0.5.1-alpha generates
consumer.cmake:import gpuin a consumer project compiled against system Vulkan headers too old for the generated 1.4 surface; the export now carries the newest discovered SDK include (unconditionally — never gated on that build's own device options) and the native frameworks on Apple.cheatah-gpu-linalg v0.4.2-alpha writes
consumer.cmake(and its exported volk archive path) to its OWN binary dir instead of the root build tree — the per-extension path the toolchain checks now actually sees it, and two extensions can no longer collide at the consumer's root.biome's table gains 0.5.2-alpha (0.5.1-alpha flips to supported); the canonicalstandards/file and the CLI expectations follow — including keeping the 0.1.0-alpha fixture honest at THAT standard's tags.
v1.11.1-alpha (2026-08-14) — the acceptance test earns its keep
Hours after 0.5.0-alpha shipped, its own acceptance test — the fresh-directory e2e that consumes every member from GitHub through biome — caught two consumer-context defects no per-repo gate could see, because they only exist where the members MEET. Biome Standard 0.5.1-alpha (a PATCH: fixes only, no API change anywhere) carries the repaired pair: cheatah v1.11.1-alpha + cheatah-gpu-linalg v0.4.1-alpha, with cheatah-gpu, cheatah-plot, and cheatah-space unchanged.
cheatah_add_programwires an extension's consumer interface into the purrc compile. The module search now also covers apurr/-rooted module directory (cheatah-gpu-linalg's layout), and an extension whose build generatesconsumer.cmakegets its flat-variable interface —<EXT>_CONSUMER_{INCLUDES,CXXFLAGS,LIBS}— translated into--cxxflag/--linkflags with real build-order edges.import gpulinalgnow compiles AND links in a biome project without the program knowing any backend detail.cheatah-gpu-linalg v0.4.1-alpha ends its build at the consumer surface when fetched as a subproject (its dev battery no longer configures inside a consumer's
_depstree) and completes the consumer flat-variable protocol with_CONSUMER_CXXFLAGS.biome's table gains the 0.5.1-alpha entry (0.5.0-alpha flips to supported); the canonicalstandards/file and the CLI expectations follow.
v1.11.0-alpha (2026-08-14) — the five-member Biome Standard
Biome Standard 0.5.0-alpha is the first standard to name a working combination of ALL FIVE ecosystem members — the toolchain plus cheatah-gpu v0.5.0-alpha, cheatah-gpu-linalg v0.4.0-alpha, cheatah-plot v0.1.0-alpha, and cheatah-space v0.1.0-alpha, every tag live with a gate-proven release behind it. A MINOR bump: membership is additive, and every program written against 0.4.0-alpha compiles and behaves identically. From here, each standard releases a tested-together set of all five.
The Biome Standard, grown and proven
biomeregisterscheatah-gpu-linalg;known_standards()gains the 0.5.0-alpha entry (0.4.0-alpha flips to supported);standards/biome-standard-0.5.0-alpha.tomlis the canonical append-only file, byte-checked against the in-source table; thelistandstandardsname columns widen for the longest member. The CLI suite covers the new membership: the three new members ADD cleanly at the standard's tags, and a project pinned to 0.4.0-alpha still gets the truthful refusal.scripts/test-standard-e2e.sh— the acceptance proof: an EMPTY directory, cheatah env cleared,biome init→ add every member → CPM fetches each member from GitHub BY TAG (real network) → build → one program exercises all five members (the gpu dispatch math, a gpu-linalg device sum behind the honestavailable()probe, a plot rendered to a byte-verified PNG, the space.time J2000 round trip) →RESULT: PASS. Wired as releasing checklist step 7 and the on-demandstandard-e2eworkflow.
One documentation site for the whole standard
The generator (
docs/gen-cheatah/gen.purr) now builds per-extension SUBSITES —docs/html/<ext>/with each package's OWN nested sidebar, landing page (version badge, description, dependency line, module table), releases page rendered from its CHANGELOG, and a scoped search index — plus a topbar package switcher on every page andstandard.html: the current standard's member table, the append-only history, a static dependency diagram generated from the member manifests, and a latest-release rollup linking every member's release notes.Single source of truth: the generator IMPORTS
pkg-manager/biome.purr, so badges, tables, and the rollup derive from the same in-source standard tablebiomeships — the old hardcoded badge string is gone. Extension classes/structs/concepts now render (they were dropped before). 292 pages; every internal link across the tree verified resolving; all URLs relative, so one host serves the whole site from any root.
TLS 1.3 server: the public-CA world
The TLS 1.3 server now speaks the public-CA world: tls.accept accepts an ECDSA P-256 leaf alongside Ed25519 (P-256 is what Let's Encrypt issues, so a browser-trusted HTTPS server is now pure cheatah), reads the private key from PKCS#8 or SEC1 PEM, sends the whole certificate chain from a fullchain.pem (previously only the first block went out — a chain silently lost its intermediates), honors the client's signature_algorithms per RFC 8446 §4.4.3, and refuses a cert/key mismatch at startup with a precise error instead of failing opaquely at the first client. p256 gains rs_to_der (the DER signature encoder TLS and X.509 carry — the exact inverse of the existing parse). Validated both directions against OpenSSL: new system tests cover the ECDSA handshake, full-chain emission against a client that trusts only the CA, and every new pre-flight refusal.
purrc: library modules survive multiple translation units
Library-mode free functions are now emitted inline. A function whose parameters all lower to concrete types is not a template, so a second including TU was an ODR violation (multiple definition at link) — found the day cheatah-plot's multi-file C++ test harness included one generated module from five TUs. Templates and constexpr functions were already implicitly inline; the emitted-source test asserts the new form.
Docs
docs/biome.md: the 0.5.0-alpha pins and a worked "From an empty directory to a plot" example — the exact commands and program the e2e acceptance test runs.docs/extensions.md: all four extensions with an honest dependency table (two stand on the standard library alone;cheatah-gpu-linalgbuilds oncheatah-gpu;cheatah-plotbuilds on both — headless, no windowing dependency).docs/mainpage.md: the documentation-examples convention (@par Exampleblocks with compile-verified programs — every public cheatah-plot function carries one today).
v1.10.0-alpha (2026-08-13) — regex, raced against RE2
The regex module now races Google RE2 — the engine whose lazy-DFA design it follows — alongside std::regex and Boost.Regex, over identical inputs in stdlib/regex/bench/: ~90 cases spanning compile, search, full_match, find/find-all, realistic corpora, size sweeps, tiny-input latency, and catastrophic-backtracking inputs up to 64 MB. Every timed case is output-verified across all four engines before anything is measured, and a differential suite (rxdiff) checks cheatah against RE2-as-oracle on thousands of generated inputs. The tally at pinned medians of 7 repetitions, across all 83 cases: vs RE2 69 faster / 14 parity / 0 slower (every parity row is a memory-bandwidth-bound scan no engine can win); vs std::regex 64 / 0 / 0 over the cases it can run at all; vs Boost 61 / 2 / 1 — the loss, compiling a 64-byte pure literal, is analysis Boost skips and match time repays.
The matcher work behind it — the public API is unchanged (compile/search/full_match/ find, leftmost-longest, the same error strings):
Unanchored
searchis one forward pass — the compiled-in.*?prefix tracks every start position in a single DFA state, so the old per-candidate rescans (O(n²) worst case) are gone: O(n), always, withmemchr/required-literal/first-set skips while no partial match is alive.$runs backward. A pattern anchored only at the end runs a reversed program from the end of the input;1274$over 4 MB went from ~1.4 ms to ~8 ns — the one shape Boost used to win.One load per byte. Transition entries are row byte-offsets, the accept flag lives inside the row, start states are cached in the pattern — a warm search allocates nothing.
Self-loops are skipped, not stepped. Start-state self-loop bytes are learned into a LUT; any state that maps a byte onto itself lets the whole run of that byte be jumped 8 bytes at a time. The 16 MB adversarial rows fell from ~30 ms to under 1 ms.
A parse error from inside a group (the depth cap, a bad metacharacter) is no longer overwritten by
unbalanced '('— the inner, more precise message survives.
Hardening found while auditing the new code: pattern length is now capped at 64 KiB ("pattern too long") — compile() spends ~40 bytes of program per pattern byte, so an unbounded hostile pattern was a memory-amplification DoS the depth cap and state budget never covered. The audit's clean verdicts (bounds, cache-growth pointer discipline, skip soundness) are recorded in SECURITY.md's standing review.
regex.cpp is now compiled directly into the unit-test binary and sits in the 100% line+function coverage gate — previously it was exercised only through subprocess e2e tests and invisible to coverage. The bench project pins RE2 + Abseil + Boost by commit; RXBENCH_ASSERT=1 exits non-zero if any case is slower than RE2, and RXBENCH_TABLE=<path> writes the full comparison table (published in stdlib/regex/README.md). Losses are reported, not hidden.
v1.9.0-alpha (2026-07-27) — macOS is checked, not claimed
cheatah has said it "builds and runs on macOS (Apple Silicon)" since v1.5.0-alpha. Nothing checked, and by this release the claim was false in five separate ways. This release makes it true and adds the CI that keeps it true — the repository had no workflows at all until now, while every downstream consumer of it had a macOS lane.
A seeded RNG now means the same thing on every platform. std::mt19937_64 is fully specified, but uniform_real_distribution, uniform_int_distribution and normal_distribution are implementation-defined — so libstdc++ and libc++ turned one identical engine stream into different numbers. seed(42) printed one answer on Linux and another on macOS. The three mappings are now written out in cheatah itself: the top 53 bits scaled by 2⁻⁵³ for a canonical double, rejection sampling for an unbiased inclusive integer range, and Box-Muller for the normal (its spare value deliberately discarded, so a call never depends on how many calls came before it). Validated over two million draws — the normal lands at mean 100.012 / sd 14.992 with 68.27% inside one sigma.
This changes the numbers your seeds produce, on every platform. Sequences were only ever reproducible within one standard library; they are now reproducible everywhere, which is the guarantee seed() was always documented to give.
The macOS build defects, each found by the new CI within minutes of it existing:
explicit_bzero— added in v1.8.0-alpha's security audit; Apple's libc does not have it. The key wipe now goes through one helper that keeps the property the audit wanted (the optimizer may not delete it) with no platform support required.std::jthread— used only for join-on-destruction, and gated behind libc++'s experimental library on Apple. The handle already joined in its own destructor, so the dependency bought nothing; it isstd::threadnow.Shared libraries did not link their dependencies. Modules linked only their object target, which propagates headers but not links. An ELF
.sotolerates undefined symbols; a Mach-O dylib does not.add_cheatah_librarygrows aDEPENDSargument that wires all three artifacts.std::from_chars— its floating-point overloads are shipped deleted by Apple's libc++. The JSON scanner selects on__cpp_lib_to_charsand falls back tostrtodwhere they are absent.Under a sanitized build, purrc now passes the sanitizer flags to the programs it compiles — which both fixes the link and means the sanitizer lane covers the code purrc emits.
New: a macOS lane that builds and tests on real Apple Silicon on every push, plus a monthly AArch64 performance lane that finally measures what docs/performance.md has owed since v1.5.0.
v1.8.0-alpha (2026-07-26) — authenticated encryption without an allocator
ChaCha20-Poly1305 grows an allocation-free surface. The string-returning forms are still the ones you want in ordinary code, but they allocate — a result string and a temporary MAC-input buffer — which rules them out of the places that forbid allocation outright: signal handlers, embedded targets, and hot loops that already own their memory. The new _into forms take caller buffers and allocate nothing at all, and they are byte-for-byte identical to the forms they mirror.
AEAD —
aead.chacha20poly1305_encrypt_into/chacha20poly1305_decrypt_intowrite into a caller-provided buffer, allocate nothing, and are async-signal-safe (no allocation, no locks, noerrno). Both may alias their input to work in place. Decryption verifies the tag in constant time before any plaintext reaches the caller's buffer, so a tampered message never leaves a partial decrypt behind.Poly1305 is now an incremental core (
init/block/finish), with the one-shot form reimplemented on top of it — both paths are provably the same arithmetic rather than two hand-kept-in-sync copies. Because the AEAD's MAC input pads every segment to a 16-byte boundary, no Poly1305 block ever straddles a segment, which is what lets the tag be streamed straight fromaad + ciphertext + lengthswith no concatenation buffer.The equivalence is tested, not asserted: byte-identical output to the string forms over the RFC 8439 vector and 200 randomized sizes chosen to straddle the 64- and 16-byte block boundaries, plus tamper rejection for every single bit flip in the message.
v1.7.0-alpha (2026-07-25) — errors grow a kind, the Biome Standard, and documentation you can trust
Exception handling becomes real error handling: errors carry a kind, except can match on it across multiple handlers, finally runs on every exit path, and re-raise works — no more silent terminate. The ecosystem gains the Biome Standard: one semantic version naming the set of component releases tested to work together, with an append-only definition, a source-retention guarantee, and biome resolving every fetched tag from it. And the documentation contract stopped being a convention: every @alloc/@complexity claim in the standard library was audited against the implementation — transitively, callees included — and a new gate now enforces the tags forever.
Language — errors carry a kind
raise/except/finallymatured: errors carry a kind, multipleexcepthandlers match on it,finallyruns on every exit path (including unwinding), and re-raise propagates the original error. A failing program now reports its error instead of silently terminating.index()returns a const reference (no more deep copy on every subscript, unblocking the optimizer); list slicesmemmove; Python floor-mod on integers; user programs unroll.
The Biome Standard — one version for the tested-together ecosystem
standard = "0.1.0-alpha"incheatah.tomlis now the one version users track. It names the exact toolchain + extension releases tested to work together (this release: cheatahv1.7.0-alpha+ cheatah-gpuv0.5.0-alpha); biome resolves everyGIT_TAGit writes from the standard, refuses extensions that aren't members of your standard, and gains abiome standardscommand. The old hardcodedv0.1.0extension pin is gone;[cheatah] versionsurvives as a manual toolchain override.The standard's major version is a promise about user code: it only ever increments when programs cannot carry forward (a forced security change, or a language change so fundamental the ecosystem's APIs all moved). Member-to-member breakage absorbed inside the set is at most a minor. The full contract, worked examples, and the community practices that keep majors rare are in
docs/biome.md.Nothing is ever stranded: standards are append-only (
standards/*.toml, drift-checked against biome's in-source table by QA gate stage 3d); release tarballs attach to every GitHub release;scripts/archive_standard.shsnapshots a whole standard (definition + git-archive of every member at its pinned tag); and a standard is only ever deprecated for an unpatchable security flaw, with a public advisory. biome itself is0.2.0-alphaand finally has tests (a 12-test CLI round-trip suite).
Documentation — audited to be true, then gated to stay true
Every
@allocand@complexitytag in the stdlib was verified against the implementation, transitively — a function's tags now account for everything its callees allocate and cost. Dozens of falsehoods fixed, among them: HMAC/HKDF "fixed scratch buffers" that are really message-sized;hkdf_expandwas notO(length); ed25519signruns two base-point multiplications and hashes the message twice;regexunanchored search is worst-case O(n²) (still ReDoS-immune — never exponential); TLSrecvdrains every buffered record, and a custom CA file is parsed per call;svdvalsallocates the full SVD workspaces; thememorymodule's request/acquire docs described blocking on the wrong side. Dead@testreferences (tests that never existed) were fixed across the library — 11 inbuiltinsalone.A new
@concurrencytag documents blocking behavior and thread-safety where it matters (memory, thread, socket, tls, websocket, random, io), and@warningmarks real hazards (nonce reuse, TOCTOU,insecure=true, shell interpretation).scripts/doc_tag_lint.shjoins the QA gate: every public stdlib function must carry@complexity,@alloc, and a@test/@crtest/@systestlink, or the push is blocked. The documentation site now also covers extension APIs — cheatah-gpu's fullgpu.*surface joins the sidebar and search index.The regex audit found (and this release fixes) a real engine bug: patterns that can match empty at end-of-input (
a*$) never matched.
Coverage — the denominator now tells the whole truth
parsers.html, the compiled JSON DOM (parsers/json/json.cpp), andsyswere shipped but invisible to the 100% gate — never compiled into a coverage binary, so the green 100% silently excluded them. All three now carry full suites (tokenizer/entity edge cases and a malformed battery for HTML; a 23-input rejection battery and the 1000-deep nesting cap for the JSON DOM; argv fail-safes for sys) and sit inside the measured denominator at 100% lines and functions.
Networking — faster downloads
Download throughput fixed: connected sockets are tuned, hardware AES is preferred for TLS records, and buffered records are drained per read. New loopback/net benchmark harnesses (
scripts/tls_loopback_bench.sh,scripts/net_bench_compare.sh) keep the numbers honest.
Linalg
Real
conj_transposeinstantiations ship, and the out-param kernels validate shapes allocation-free.
v1.6.0-alpha (2026-07-17) — a faster transpiler, a parallelized QA gate, and a full security audit
The purrc transpiler is faster and the QA gate is dramatically faster — both without changing a single byte the compiler emits or a single test it runs. And a full-surface security audit hardened the whole standard library: twelve findings fixed, headlined by making ECDSA signing constant-time and closing two integer-overflow heap writes in linalg.
Compiler — a faster transpiler, byte-for-byte identical output
The lexer is ~1.6× faster and codegen ~1.2× faster, cutting the full frontend transpile of the largest shipped module (
requests) by ~19%: the token stream is pre-reserved, character classification is a branch-free ASCII test instead of locale-aware<cctype>, the codegen symbol tables are hash maps instead of red-black trees, and the parser dispatches keywords through one guarded switch. A golden-master harness asserts the emitted C++ is byte-identical across a corpus of real programs, so every optimization is provably behavior-preserving.
QA gate — same checks, a fraction of the wall-clock
The gate runs its test, sanitizer, and benchmark stages in parallel (
ctest --parallel, a gtest-sharded Valgrind, a sharded benchmark smoke pass and a pair-sharded perf gate) and pipelines the build-independent stages (coverage/docs/cppcheck) behind the build — the same tests, the same-O3 -march=nativebinaries, just concurrent. On a 20-core box the unit suite dropped ~139 s → ~15 s and Valgrind minutes → ~31 s. Accachelauncher absorbs the repeated builds when present.
Security — full audit, twelve fixes (see SECURITY-AUDIT-v1.6.0.md)
ECDSA signing (P-256 / P-384) is now constant-time. The secret-scalar comb no longer branches on, or indexes the table by, the secret nonce/key bits, and its point arithmetic is branch-free — closing a local timing side-channel that could leak nonce bits. Verify paths (public data) are unchanged; a differential self-check confirms the constant-time ops match the reference on every case.
Two
linalginteger-overflow heap writes are fixed.matrix_powerandkronformed a product of two dimensions before the overflow check, so a wrap under-allocated and the kernel wrote out of bounds (ASan-confirmed formatrix_powervia a broadcast view); both now route through the checked multiply and throw on overflow.The JSON and XML parsers no longer overflow the stack on valid deep input — JSON caps container nesting during the validated parse (its owning tree's destructor was the sink); XML's
text()is iterative.requestschunked/header decoding is O(n) instead of O(n²), closing a remote CPU-exhaustion DoS.regexbounds its parser recursion, epsilon-closure, and lazy-DFA cache against crafted patterns (match time was already linear).Supply-chain hardening for the coming package manager:
purrcnow allowlists a module header'scheatah-link:flags to genuine linker inputs (a malicious dependency could otherwise run code on the consumer's build host), and env-enforced strict mode refuses an argv trust-anchor substitution. Plus bounds on the RSA verify exponent, the AEAD single-message length, and the TLS handshake flight.All prior hardening (TLS X.509 validation, WebSocket frame bounds,
requestscaps, P-256 on-curve, RSAe=1) was re-verified intact, and every fix ships under the 100%-coverage + ASan/UBSan/TSan/ Valgrind gate.
v1.5.0-alpha (2026-07-15) — cross-platform macOS/Apple Silicon, a concept-templated linear-algebra library, and TLS chain hardening
cheatah now builds and runs on macOS (Apple Silicon) as well as Linux; the linalg library is rewritten as a single concept-templated form — one definition per operation over both the element and the container (real/complex/host unified) — with no loss of performance; and TLS gains multi-SAN matching plus ECDSA P-384 / SHA-384 certificate-chain validation.
Cross-platform — macOS / Apple Silicon
The toolchain and standard library build and run on macOS arm64.
os.module_ext()returns the platform module suffix (.dylib/.dll/.so), used by the launcher, thebiomepackage manager, and purrc's non-CMake fallback (which also uses-mcpu=nativeand drops-lmon Apple).Hardware crypto on Apple Silicon: the AES-GCM path documents and asserts the ARMv8 AES + PMULL NEON route (every arm64 Mac ships FEAT_AES/FEAT_PMULL) alongside x86 AES-NI/PCLMULQDQ.
getentropyreplacesgetrandomfor the CSPRNG — portable across Linux (glibc ≥ 2.25), macOS, and BSD.float()correctness:to_floatis nowtemplate <Number T>, sofloat(0.95)can never route through an integer overload and truncate to0.The QA gate skips Valgrind on Darwin (broken on Apple Silicon; ASan/UBSan cover it); the TLS system test prefers a Homebrew OpenSSL peer over macOS LibreSSL.
Linalg — one concept-templated definition per operation
The whole
linalglibrary is rewritten in a two-layer template form — every routine istemplate <Field T, template <typename> class Array>overArray<T>, so real and complex (and host vs a future device container) are ONE definition instead of hand-duplicated overloads:matmul,dot/vdot/inner(aConjenum),solve/det/inv,qr/svd, the eig family, and the rest. Public names and results are unchanged; mixing element types or containers is now a compile-time error via the concept constraints.Shared numeric primitives: the multi-accumulator reduction (
reduce_lanes) is factored once inndarrayand reused bysum,dot,trace, and Householder QR; the LU preamble folds into onelu_prepare.No performance regression — the templates monomorphize to the same machine code; several ops are marginally faster (a dropped throwaway zero-fill).
linalgstill matches/beats Eigen on dense routines andfixarraystill beats GLM on the fixed-extent types (the QA perf gate).
TLS fix — every subjectAltName is matched, not just the first
X.509 SAN parsing stopped after the first
dNSName(a shared loop-bound variable in the DER walk), so any host matched by a later SAN entry was refused as "certificate is not valid for host". Multi-SAN certificates are the norm on CDN-shared hosts —https://fastly.com,https://www.fastly.com, andhttps://github.ioall failed while the first-SAN host worked. All SAN entries are now parsed and matched (CheatahX509.ParsesAllSubjectAltNames,.MatchesLaterSan).
TLS feature — ECDSA P-384 and SHA-384 certificate chains validate
New
p384module: NIST P-384 (secp384r1) ECDSA verification, sharing a width-generic, concept-constrained template core (p256/ec_core.hpp) withp256— the same battle-tested Montgomery arithmetic and Jacobian group law, instantiated at 6 limbs. Verified against the RFC 6979 A.2.6 known-answer vectors (SHA-384 and SHA-256, pinning both hash-truncation semantics).hashlib.sha384/hashlib.sha384_digest: SHA-384 via the existing SHA-512 core with its own IV, NIST-vector- and OpenSSL-cross-checked.Chain validation now verifies
ecdsa-with-SHA384andsha384WithRSAEncryptionsignatures, dispatching the ECDSA curve by the issuer key's named-curve OID and the hash by the signature OID — real CA chains mix them (Sectigo signs a P-256 intermediate with a P-384 root). api.github.com and cdn.jsdelivr.net now validate; SHA-512 and rsassa-PSS chain signatures still fail closed by design.ecdsa_secp384r1_sha384(0x0503) is offered insignature_algorithmsand verified in CertificateVerify, so P-384 leaf certificates handshake too (liveopenssl s_serversystem test).
v1.4.0-alpha (2026-07-10) — smaller memory footprint: opt-in sized integers, plus fixarray + from-import ergonomics
A footprint-and-ergonomics release. Integers gain opt-in fixed widths (i8…u64) so a column, struct, or ndarray can store 1–4 bytes per element at the same compute speed, with int still the 64-bit default. ndarray.astype builds narrow-element arrays, the fixarray fixed-extent vector/matrix module becomes callable directly from cheatah (including module-qualified type declarations), and from … import … brings in structs, enums, and functions as prefix-free first-class objects.
Opt-in sized integer storage types — smaller memory footprint, same speed
Declare a narrow width where footprint matters. Any type annotation may now be an explicit-width integer —
i8/i16/i32/i64,u8/u16/u32/u64— so alist<i32>stores 4 bytes per element instead of 8, adict<str, u8>keeps 1-byte values, anarray<i16, N>andndarray<i16>carry narrow elements, and astructofu8fields packs (twou8s → 2 bytes, not 16). Proven in-language withsizeof. Previously every integer was a 64-bitlong long, everywhere.intis unchanged and still the default. It stayslong long(64-bit), so standalone integers — loop counters,++/--, literals — never change or slow down. Narrowing is opt-in per declaration; nothing narrows implicitly.Three spellings, one type. Each width is nameable as our short form (
i32), the long form (int32), or the original C library name (int32_t) — all the same<cstdint>exact-width type by construction, so use whichever you prefer.Zero runtime cost, portable. A width lowers straight to a
std::int32_t/std::uint8_t/…; the storage is contiguous and SIMD-friendly and arithmetic still promotes to 64-bit for free — only the stored form is narrow. Standard C++20, no tagging/boxing/bit-packing.Semantics. Narrow storage wraps at its width and a 64-bit result truncates on store (as in C / NumPy fixed-width types); a literal initializer that does not fit is a compile-time error, the only check and it costs nothing at runtime.
arr.astype(<width>)converts an ndarray's element type — numpy'sa.astype(dtype)— sondarray.array([1, 2, 3]).astype(i16)is abasic_ndarray<std::int16_t>(2 bytes/element, not 8). Widening is exact; narrowing truncates at the target width; complex→real is a clear compile error.A declared narrow ndarray type drives construction:
let a: ndarray<i8> = ndarray.array([…])converts for you (no explicit.astypeneeded). Narrow (i8/u8) elements print as numbers, matching the rest of the language. Narrowing/widening follows C / numpy fixed-dtype semantics — signed narrowing wraps two's-complement, unsigned is modulo 2^bits, float→int truncates toward zero — and every case is covered by tests asserting the exact printed values.
The fixed-extent vector/matrix module (
import fixarray) can be used directly from a.purrprogram: construct (fixarray.vec3f(1.0, 2.0, 3.0),fixarray.Fixed<f32, 3>(…), narrowfixarray.Vec<u8, 3>(…)), and call its operations (fixarray.dot,cross,normalize,matmul,+/-/*). Previously the module was reachable only from C++.Module-qualified types now work in
letand struct-field annotations —let v: fixarray.vec3f,let m: fixarray.Fixed<f32, 3>,struct Body { pos: fixarray.vec3f }— closing a gap where a dotted type (state.State,fixarray.Fixed<…>) could only appear on function parameters/returns. Numeric template extents (Fixed<f32, 4, 4>) are also accepted in parameter/return positions.
v1.3.0-alpha (2026-07-03) — deterministic resource cleanup, ownership + native threads, first-class crypto/networking
The standard-library release: cheatah gains a with statement and owning RAII guards, an ownership/borrow engine (memory) feeding real OS threads (thread), the crypto + networking modules become first-class (and 100% tested against real peers), and pure cheatah is now provably leak-free. Copyright is held by BigBrain LLC (lead engineer and producer: Joshua Doucette, on its behalf); MIT-licensed.
New
memory.own(value) -> Owner<T>takes SOLE ownership by moving the value in (it is consumed, never copied); anOwneris non-copyable and pinned, so its object never moves and a borrow can never dangle.Tis the only type you spell — the scheduling policy is a constructor argument.Every access is a request → acquire → lease.
o.rread()/o.rwrite<priority>()return aRequest;.acquire()blocks until the owner grants aLease— the only handle to the object. Read leases are shared (they coexist); write leases are exclusive.Setters and symmetric getters, concept-gated on the owned type.
writeis a setter (never returns a mutable object):w.write(value), and — deduced —w.write(index, v)for sequences andw.write(key, v)for maps.readmirrors it:r.read(),r.read(index),r.read(key), plusr.read_front()/r.read_back()where the container has them.readalways returns a reference.A hand-rolled priority reader/writer engine (one mutex + condition variable over explicit state, since
std::shared_mutexcan't honor priorities): drain-before-write (a write waits for readers to release; a reader looping onvalid()yields), a priority queue of waiting writes (o.rwrite<10>()jumps ahead), and immediate-writes (o.rwrite<memory.immediate>(), any negative priority) that preempt an active cooperating writer, do their work, and let it resume. Deterministic-final results under nondeterministic interleaving; ASan/UBSan-clean.
regex.find(...).textis an ownedstr(the library copies the matched bytes), so it is always safe to keep, pass, or return — even off a temporary input. The old borrowedview<str>(which dangled on a temporary — a use-after-free) is removed entirely, along with theview<T>type from the prelude..begin/.endoffsets remain for callers who want to slice their own input zero-copy. cheatah is C++ — somewhere we own the string.
New
thread.spawn(f, args...) -> Threadruns a cheatahfnon a new thread; the returned guard is move-only and joins at scope exit (plainlet,with, or unwinding — every thread finishes beforemainreturns). Nodetach, by design: the host unloads the program's module right aftermain, and an unjoined thread would break the deterministic- cleanup guarantee.Copy-in by default; an
Ownerby reference. Every copyable argument is copied into the thread (a worker owns its values — you cannot accidentally share a plain value); a movable rvalue is moved in; a pinned, non-copyablememory.Owner<T>is the deliberate exception and travels by reference — the module's design funnels shared mutable state through thememorymodule's request → acquire → lease flow. A non-copyable temporary does not compile.A worker
raisere-surfaces att.join()(catch it in-language withtry/except); an exception nobody joined for is reported on stderr by the guard's destructor.joinable()tracks the handle's lifecycle.The threading contract is documented (
docs/threading.md): races are the developer's responsibility — cheatah keeps per-thread guarantees airtight and pushes sharing towardmemory.Owner(anOwneris the lock;memory.own(false)is a stop latch).
Codegen
Template arguments inside module-qualified type annotations now map like every other cheatah type: a parameter
o : memory.Owner<int>lowers to the concretememory::Owner<long long>&thatmemory.own(0)deduces (previously theintleaked through raw). Locked in by an emitted-source assertion (StdlibE2E.ThreadOwnerParamLowering).
Quality
New ThreadSanitizer gate: a
tsanCMake preset (mutually exclusive with the ASan one) and a QA-gate stage that runs the concurrency-relevant suites under TSan (QA_GATE_SKIP_TSAN=1to skip locally) — a data race in the standard library now fails the gate.randomis per-thread: the once-shared Mersenne Twister is nowthread_local(concurrent draws from spawned workers never race;random.seedseeds the calling thread only, and each new thread self-seeds fromstd::random_device).
Docs & tooling
Dogfooding, now parallel. The pure-cheatah doc-render benchmark gains a parallel variant (
docs/gen-cheatah/gen_bench_parallel.purr) — the read- only Doxygen XML lives in one sharedmemory.Owner(coexisting read leases), workers accumulate the rendered-byte and (regex-counted) function totals intoOwners via exclusive writes, and the result is byte-identical to the single-threaded run. Four modules cooperating (parsers.xml+regex+memory+thread): ~1.8× the single-threaded speed and 4.3× CPython, deterministic. See Performance → Dogfooding.VS Code extension → 1.3.0 (aligned with the toolchain): grammar recognizes the full stdlib module list (
p256/x25519added) and highlightsconstexpr/auto; added a LICENSE file and CHANGELOG, refreshed README, dropped the committed.vsixbuild artifact.
New
with resource [as name] { … }statement — binds a resource for the block and runs its destructor on every exit path (return, break, exception). Lowers to a lean C++ scope; it is RAII, not a Python__enter__/__exit__protocol (any value with a destructor works).Owning guard types on every stateful module:
socket.Conn/Listener(viasocket.open/serve),tls.Conn(viatls.open),websocket.Client(viawebsocket.open/open_url), alongside the existingio.File. A guard held as a plainletalso closes at scope exit.
Airtight memory-leak guarantee
Pure cheatah cannot leak heap memory. The heap-allocating raw handle APIs of
tls(client_connect, …) andwebsocket(connect/connect_url, …) are now C++-only — moved totls_lowlevel.hpp/websocket_lowlevel.hpp, which cheatah's module resolver does not surface. Calling them from cheatah is a compile error; cheatah uses the guards, which release automatically. The sole in-language leak path is now acpp { … }block. SeeSECURITY-AUDIT-v1.3.0.md.socket's fd-based API stays cheatah-visible (an unclosed fd is an OS-resource leak, not heap memory); thesocket.open/serveguards are the recommended leak-safe path.
First-class crypto + networking (now tracked + 100% covered)
Six modules that had lived out-of-tree are now committed, first-class, and held to the full gate (100% line+function coverage, 100% Javadoc, ASan/UBSan/Valgrind clean):
aead— ChaCha20-Poly1305 (RFC 8439) and AES-128/256-GCM authenticated encryption, with a runtime-selected AES-NI fast path and a portable fallback.x25519— RFC 7748 Diffie-Hellman, constant-time in the secret scalar.p256— NIST P-256 ECDSA sign/verify with RFC 6979 deterministic nonces, plus SPKI/DER parsing (ES256 JWTs and TLS leaf certificates).tls— a from-scratch TLS 1.3 client (RFC 8446) built only on the cheatah crypto modules — no OpenSSL. TLS_CHACHA20_POLY1305_SHA256 / TLS_AES_128_GCM_SHA256, X25519, SNI; authenticates the server leaf (Ed25519 / ECDSA P-256 / RSA-PSS) and refuses a peer it cannot verify.websocket— an RFC 6455wss://client over the tls stack (client masking, fragmented message reassembly, transparent ping/pong).requests— an HTTP/1.1 client (query params, custom headers, redirect following, chunked/Content-Length/EOF framing, per-request timeouts,https://) and the first standard-library module written in cheatah itself (requests.purr).Coverage is measured against real peers, never a mock:
tlsagainstopenssl s_server(each leaf-cert algorithm and record cipher),websocketagainst a real Nodewsserver,requests/socketover a loopback server — all merged into the 100% line+function gate.parsersis now hand-written C++ (its.purrwas retired).PEM parsing fails closed: X.509 certificate bodies decode with a strict base64 mode — a non-alphabet, non-whitespace byte rejects the certificate instead of decoding to garbage — and the crypto modules now share hashlib's single canonical
to_hex/from_hex(the duplicated per-module hex helpers are gone).
Compiler (
Pure-cheatah modules with
structs are now fully documented: purrc emits Javadoc for struct fields, the synthesizedparsers.json.schema<>specializations,module_abi, and default-argument forwarding overloads — so a.purrmodule passes the 100%-Javadoc gate.
Docs, quality, and copyright
Security audit (
SECURITY-AUDIT-v1.3.0.md): every heap/handle allocation site is enumerated and shown to be value-typed or RAII-guarded; ASan+UBSan and Valgrind report 0 leaks / 0 errors over the guards.@complexity/@alloctags audited for correctness across the whole stdlib — e.g.linalg.eigcorrected to O(n⁴) (per-eigenvalue inverse iteration),string.splitto O(n·m), and out-parameter products now note the scratch packing of non-contiguous operands.Coverage gate merges the in-process real-peer system tests and honors a sparing, justified
// LCOV_EXCL_LINEmarker for genuinely-unreachable defensive branches (three intls: a refusal path a conformant peer cannot trigger, which we neither delete nor reach by mirroring a malformed server).Documentation refreshed and tightened repo-wide (module READMEs incl. new
tls/requestspages, the porting + security guides, the generated site + VS Code hover DB); every doc page now carries the BigBrain LLC copyright footer.Docs site restructured: a three-section sidebar — Guides, Language parity (a new cheatah ↔ C++ page covering the C++ features usable first-class without a
cpp { … }escape hatch, beside cheatah ↔ Python), and Modules with submodules (os.path,parsers.json, …) as collapsible dropdowns; a module's classes are now listed on the module's own page instead of a separate sidebar column. New biome (package manager) and imports (module resolution) guides, plus richer transpiler examples (multi-module linking; aninterfacelowering to a C++20 concept).Copyright held by BigBrain LLC (
LICENSE,NOTICE, README) — lead engineer and producer Joshua Doucette, on its behalf. The README links the public ecosystem packages (cheatah-space,cheatah-gpu,cheatah-plot).
v1.2.0-alpha (2026-06-10) — pure-cheatah library modules, language ergonomics, pretty output, editor diagnostics
A large feature release: cheatah can now build standard-library modules written in cheatah itself, the language gained several ergonomics + safety rules, output is readable by default, and the editor surfaces real compiler errors as you type. Additive — existing programs keep working.
Pure-cheatah library modules (
purrc --emit-librarycompiles a.purrinto an importable module living innamespace cheatah::<name>— a signed header (+ a compiled archive in opaque builds), verified by a consumer'spurrcbefore it compiles against it. Opaque by default (ships only the API, hides concretely-typed implementations inlibcheatah_<name>.a);--transparentinlines the generated C++ source into the header (what the first-party stdlib uses, so the true code is always visible). purrc verifies a module's SHA-512 (and, withCHEATAH_TRUST, its Ed25519 signature) and fails closed on a tampered module.New first-party
parsersmodule — the first stdlib module authored in.purr(empty for now; the mechanism is set up).cmake/CheatahModule.cmake(cheatah_add_module) builds it; the QA gate guards that its committed header stays in sync with its source.biome extension template is now a
.purrlibrary (opaque by default), andcheatah_add_program(… EXTENSIONS …)wires a fetched extension's module dir onto purrc's search path.
Language ergonomics + safety
An unset value is a bug that does not compile. A struct is built with a C++20 designated initializer
Point({.x = 1})(fields you omit default-initialize, never garbage; an unknown field is an error). Aletmay be declared with no value (let total,let total: float), but the compiler tracks it: a never-assigned variable is removed, and one used before it is definitely assigned — or only conditionally assigned — does not compile.Multi-line expressions. Newlines inside
( )and[ ]are now insignificant (Python implicit line continuation), and the generated C++ preserves the source's multi-line layout (readable.gen.cpp).str()is a builtin (Pythonstr());"x" + valueauto-stringifies to the same minimal C++ as"x" + str(value), andstr(str(x))collapses tostr(x).Dead-variable elimination is on by default (unused, non-returned locals are removed, side-effecting initializers preserved); opt out with
--no-remove-variablesor the umbrella--no-optimize-cpp.--validate-cppinfrastructure (a post-codegen, pre-compile hook + acheatah::purrc::CppValidationException), wired but not yet enforcing.
Readable-by-default output
io.printpretty-prints: a struct renders on indented multiple lines (recursively), and a large NDArray is abbreviated with…(numpy-style edge items). Newio.rprintprints the raw/compact form (a full, unabbreviated array). NDArray is now directly Streamable (operator<<), and every cheatah struct of streamable fields gets an auto-generatedoperator<<.
Editor (VS Code extension)
Live diagnostics: the extension type-checks the open buffer with
purrc --checkand squiggles real errors — a forgottenlet, an unresolved symbol, a wrong argument count or type — mapped to the.purrvia#linedirectives (settings:cheatah.purrc,cheatah.diagnostics.enable).Fixed autocomplete/hover for
linalgandndarray(a perf-rendering crash had disabled the whole provider for those modules).
Tooling + tests
previously_broken/regression suite — bugs that once broke the toolchain, run FIRST in the QA gate so a reintroduction fails fast.library_module_testcovers transparent/opaque emit, import verification, and tamper-fails-closed.
v1.1.1-alpha (2026-06-10) — 512-bit module integrity + verification benchmarks
A hardening pass on the v1.1.0 integrity feature: the binary and runtime signing path is now 512-bit throughout, and the per-tier verification cost is measured, not guessed.
Signing now uses SHA-512 (512-bit), not SHA-256
The module checksum sidecar is now
<module>.sha512(sha512sum-compatible), written bypurrc --checksum/--signand auto-verified by the runtime. Ed25519 signatures already hashed with SHA-512 internally, so all of code- and runtime-signing is now 512-bit. SHA-256 stays in thehashlibstdlib module for your own applications — it is simply no longer used for signing.Breaking (alpha): a
<module>.sha256produced by v1.1.0 is no longer recognized; re-runpurrc --checksum(or--sign) to emit the.sha512sidecar.
Verification benchmarks
New
tests/benchmarks/integrity_bench.cpptimesverify_moduleper tier. Verification is paid once at load, never during execution, and is zero-overhead when off. Representativex86_64cost: a 64 KiB module is ~0.16 ms with the SHA-512 checksum, ~2.4 ms with a strict Ed25519 signature, ~4.4 ms with the signed runtime manifest as well. The SHA-512 pass scales with module size; each Ed25519 verify is a roughly fixed ~2 ms (the from-scratch crypto is audit-oriented, not throughput-tuned). The numbers now back the Security page's Performance section and a table inSECURITY.md.
v1.1.0-alpha (2026-06-10) — module integrity: from-scratch crypto + signed binaries
cheatah can now verify a compiled module against corruption and tampering before the runtime loads it, backed by cryptography implemented from scratch in the standard library (no external dependency). Additive and opt-in — existing programs are unaffected and pay nothing unless they turn it on.
New stdlib crypto (no external dependency)
hashlibgains SHA-512 (sha512) alongside SHA-256, plus raw-digest forms (sha256_digest/sha512_digest, like Python's.digest()vs.hexdigest()).New
ed25519module — RFC 8032 public-key signatures (generate,public_key,sign,verify), hand-implemented and validated byte-for-byte against OpenSSL and the RFC 8032 known-answer vectors. Strict verification rejects non-canonical signatures (S ≥ L). The runtime links this same code to verify modules.os.urandom(n)— a CSPRNG (getentropy/BCryptGenRandom), fail-closed.
Module integrity (opt-in, fail-closed)
purrc --keygen <prefix>writes an Ed25519 keypair (secret key created0600);purrc --sign <key>signs a built module (writes<mod>.sig);purrc --checksumwrites a sha256sum-compatible<mod>.sha256.The runtime verifies before
dlopen: a.sha256sidecar is auto-checked for corruption; withCHEATAH_VERIFY=strict(or--verify) a valid.sigfrom a key in the trust file (CHEATAH_TRUST/--trust) is required, else the module is refused. Verification binds the load to the exact bytes it hashed (/proc/self/fd, no verify-then-load race), is non-downgradable by argv, caps the module size, and is off by default with zero overhead. See the Security guide; the runtime header documents the per-call@complexity/@alloc.C-runtime compatibility check.
purrc --runtimerecords the build's CPU arch, glibc version, and libstdc++ ABI in a<mod>.rtmanifest; the runtime checks it against the live host before loading (e.g. refuses "module needs glibc >= 2.39, but this host has glibc 2.31" instead of a crypticdlopenfailure).purrc --sign-runtime <key>signs the manifest with a key separate from the code-signing key (--trust-runtime/CHEATAH_RT_TRUST), so code authenticity and build-runtime provenance are vouched for independently and the two keys are not interchangeable.
Tooling
purrcandcheatahnow accept--help/-h(usage to stdout, exit 0), in addition to--version/-v.The QA gate now runs cppcheck (performance + security) across the repo.
The VS Code extension highlights cheatah's custom Javadoc tags (
@complexity/@alloc/@test/@crtest/@systest) like the standard ones.
Performance & codegen
ndarrayresult buffers are now allocated uninitialized (a small default-init allocator) instead of being zero-filled and then immediately overwritten. The throwaway zero pass was invisible on compute-heavy ops but dominated the bandwidth-bound ones: element-wiseaddgoes from 1.5× slower than NumPy to 1.2× faster (16384 elements),sqrtties NumPy at largen, andexp/sinwiden to ≈4–7×. Thelinalgresults that are as big as their own work —outer,kron, the conjugate transpose — build straight into that buffer and are moved in zero-copy, soouternow beats Eigen (was behind) andkronis ~1.5× faster than before. Verified ASan + Valgrind clean.Generated code now
#includes a singlecheatah.hppprelude instead of repeating a dozen standard-library#includes (plus the export macro) at the top of every file. The built-in runtime already pulled in most of them; modules still map one-to-one to their own headers.
Docs
New “Why cheatah?” guide (the motivation: a transpiled, statically-typed, memory-safe, transparent language for an AI-threat world), reached from the overview.
The Security page documents the integrity feature and threat model; the Performance page now compares against both NumPy and Eigen and is precise about the few routines where their tuned kernels edge ahead. Benchmarks regenerated.
Notes
A security audit of the feature was performed; findings (downgrade hardening, fd-bound strict loading, size caps, canonical-
Srejection, key-file permissions) are fixed.
v1.0.0-alpha (2026-06-09) — per-module namespace aliasing in generated code
The transpiler now shortens every module reference in the C++ it emits. This is a codegen-shape change — the generated .gen.cpp looks different (the runtime behavior of any program is unchanged) — so the version steps to 1.0.0.
Codegen — every module gets its own short namespace alias
The whole program is emitted inside a dedicated
namespace cheatah_program, and the exported entry point becomes a one-lineextern "C"trampoline (PURR_EXPORT void purr_main() { cheatah_program::run(); }). This wrapper is what makes the aliasing below safe.Each imported module — plus the always-available
builtins— gets its own distinct alias at the top of that namespace (namespace io = cheatah::io;,namespace ndarray = cheatah::ndarray;,namespace linalg = cheatah::linalg;,namespace builtins = cheatah::builtins;, …). The body then readsio::print,linalg::solve,builtins::leninstead of repeatingcheatah::io::…everywhere. For examplecheatah::io::print(std::string("solve A x = b ->"), cheatah::ndarray::to_string(cheatah::linalg::solve(a, b)))is nowio::print("solve A x = b ->", ndarray::to_string(linalg::solve(a, b))).Safe against the global C library. A module whose name matches a libc/POSIX global function (
time,random,socket) is still aliased — because the alias lives insidecheatah_programand resolves tocheatah::<name>, it can never redefine the global::time/::random/::socket.Safe against program identifiers. If the program uses a module's name as one of its own identifiers (e.g. a
struct os, or a parametermath), that module is left explicit (::cheatah::os::…) so the alias can't shadow or clash with user code. A bare identifier that shadows a module name now correctly resolves to the local (fixes a latent bug wherefn bump(math)emittedcheatah::mathfor the parameter).The change is purely in emitted code; no
.purrsource needs to change and program output is identical.
Extensions
The extension-template documents the contract: a module must keep everything inside
namespace cheatah::<name>so purrc's per-module alias reaches it, and two extensions must have distinct module names.
Tests
New
NamespaceAliasingsystem-level suite (runs in the QA gate) asserts the generated C++ itself — each module aliased distinctly, libc-named modules aliased safely, and collisions staying explicit — alongside compiling and running each program.
v0.9.1-alpha (2026-06-09) — tighter string codegen, enum highlighting
A small follow-up: the transpiler emits leaner string-building code, and enum now syntax-highlights in the docs.
Codegen — self-append builds in place, in one statement
A
+-chain self-append now lowers to a single chained statement with no intermediatestd::string.head = head + "Content-Type: " + ctype + nlbecomes((head += "Content-Type: ") += ctype) += nl;— three in-place appends, no temporary per piece, and a string literal appends as a bareconst char*(sooperator+=takes it directly rather than constructing a throwawaystd::string). Chaining is used only where it's valid (operator+=returns a reference — std::string and arithmetic accumulators, the only types a+self-append fires on); a single appended operand stays a plainx += e;.
Docs
enumnow syntax-highlights in the generated docs site — the highlighter's keyword set had drifted out of sync with the lexer.
v0.9.0-alpha (2026-06-09) — enums, the
A scoped, printable enum type joins the language; a new sys module plus runtime argument forwarding gives programs their sys.argv; and biome, a CMake/CPM-based package manager written largely in cheatah, lands in pkg-manager/. The VS Code extension learns all of it, and gains hover docs for your own same-file definitions.
Language — enums
enumdeclares a scoped, type-safe enumeration that lowers to a C++enum class(not a plain Cenum):enum Color { RED, GREEN, BLUE }. Members are reached through the enum name (Color.RED), separated by newlines, commas, or semicolons, and may carry an explicit value (enum Status { OK = 0, WARN, FAIL }, with the rest auto-incrementing as in C++).Enums compare with
==/!=, work inmatch/case, and serve asstructfield and function-parameter types.Enums print for debugging.
io.print(Color.RED)showsColor.RED(Python's style); they also render insideio.formatand printed lists/dicts. An out-of-range value (e.g. from acpp { … }cast) showsColor(<n>).
Numeric core — a performance pass that now beats Eigen
A focused optimization round on linalg/ndarray, benchmarked against Eigen 3.4 (the reference single-threaded C++ dense-linear-algebra library) as well as NumPy. The recurring villains were the same few mistakes, hunted down across every function:
A heap allocation hidden in a hot predicate.
is_contiguousrebuilt the reference strides — astd::vectorallocation — on every call, and the products/reductions call it per operand. Made it allocation-free; this one fix lifted every contiguous op.Single-accumulator reductions. A lone running sum serializes on FP-add latency (the compiler can't reassociate without
-ffast-math).dot/norm/sum/cholesky/traceand the symmetric-eigensolver tridiagonalization now use several independent accumulators, so-O3 -march=nativeissues SIMD+FMA and reaches memory bandwidth.matmulre-streamed B per output row → 4-row register blocking (real and complex) reuses each B element four times.qrwalked columns of a row-major matrix (stride-n, un-vectorizable) → it now works on the transpose so the Householder reductions/updates are contiguous.The symmetric eigensolver's tridiagonalization kept the active block full-symmetric so its matrix–vector product vectorizes (no packed column-stride walk).
make_matrix/make_vectorzero-filled a full-size result buffer and then threw it away, replacing it with the computed data — a wasted O(n²) pass that dominated memory-bound ops. They now build the result directly from the buffer.
Result: on one core, cheatah matches or beats Eigen on most dense routines (matmul, inv, solve, det, the SVD, the symmetric eigensolver, dot at scale, outer, trace, norm), and now beats NumPy/LAPACK across nearly the whole library — including outer, qr, kron, and large norm, which previously lost. The honest comparison (Eigen ratios, the NumPy table) and a reproducible benchmark harness ship in tests/benchmarks/ (eigen_compare_bench.cpp) and scripts/numpy_compare.py. A few routines (blocked QR / Cholesky, the eigenvector path) remain behind Eigen's blocked BLAS-3 kernels — flagged honestly, not hidden.
Numeric core — N-dimensional
ndarray.array(...)now builds an array of any rank from a nested list —array([[1, 2], [3, 4]])is 2-D,array([[[1],[2]],[[3],[4]]])is 3-D, and so on to any depth. The shape is inferred from the nesting and the leaf type deduced; a ragged list is rejected, exactly as numpy rejects one. (Previously onlyreshapecould make a >1-D array.) Broadcasting and reductions already worked at every rank.New cross-checks: the
linalgroutines are verified op-by-op against NumPy by a system-test suite that loads editable.purrprograms fromtests/purrc/linalg_programs/.
Standard library —
New
sysmodule exposessys.argv— alist[str]of the program's command-line arguments (sys.argv[0]is the program,sys.argv[1:]the arguments), exactly like Python. Index it, slice it,len(...)it, iterate it.The
cheatahruntime forwards arguments to the program. Runningcheatah app.so one twopopulatessys.argvvia an exportedcheatah_set_argvhook thesysmodule provides; programs that do notimport sysare unaffected. purrc still emits only loadable modules — compiled code always runs under the runtime.
Tooling —
New
biomepackage manager (inpkg-manager/) — most of it written in cheatah itself (biome.purr, compiled to a module by purrc), driven by a small native launcher so it is invoked asbiome <command> <args>while its compiled code still runs only under the cheatah runtime.Commands:
init(scaffold a project with acheatah.tomlmanifest, generatedCMakeLists.txt, and a CPM bootstrap),add/remove/list(manage optional standard-library extensions —cheatah-gpu/cheatah-plot/cheatah-space), andbuild/run(drive CMake + CPM, so the whole build is handled by CMake).New
cmake/CheatahProgram.cmakehelper —cheatah_add_program(NAME SOURCES x.purr …)compiles a.purrto a module with purrc and builds a launcher that runs it via the runtime. Downstream projects fetch the toolchain withCPMAddPackage(NAME cheatah …)and use this helper. A cheatah pulled in as a sub-project no longer builds its own test suite (tests default on only when cheatah is the top-level project).Status: core flow works end-to-end (configure → purrc module → launcher, via CMake/CPM); wiring third-party extension archives into purrc's link line, and publishing the extension repos + release tags, are follow-ups.
Editor — VS Code extension
enumis highlighted (the keyword and the enum name as a type), and the extension's IntelliSense understands enums declared in your file: hover an enum type or a member (Color.RED), autocomplete members afterColor., and go-to-definition jumps to the declaration.Hover docs for your own same-file definitions. Hovering a
fn,struct,interface, orenumdefined in the open.purrfile now shows the comment written above it (its "docstring") — taking priority over the stdlib database, so a local function no longer shows an unrelated same-named header.
Project
Added ACKNOWLEDGMENTS.md crediting the open-source work (Python, the C++ standard, NumPy/SciPy/Matplotlib, BLAS/LAPACK, Eigen, GLM, …) that informed cheatah's design — the author first, then the prior art.
v0.8.0-alpha (2026-06-09) — Python-3 division + the whole library, documented
The / operator is now true division (always a float, even int / int), matching Python 3, and integer/floor division moves to an opt-in // operator. Alongside it, the docs site grew to cover the entire standard library: every module's README now ships on its page, and the linalg-vs-NumPy numbers live beside the functions they measure.
Language — division (
/is true division.6 / 4 == 1.5, and even an exact6 / 2is adouble. The operator lowers tocheatah::builtins::truediv.//is opt-in floor division, flooring toward −∞ like Python (-7 // 2 == -4,7.0 // 2.0 == 3.0) —cheatah::builtins::floordiv. As a result//is no longer a comment; comments are#only.Porting: a
/that you relied on for integer division becomes//.
Docs — the whole library on the site
Per-module READMEs now render on each module's page. The examples and prose from
stdlib/<mod>/README.mdare merged in as the overview above that module's reference, so the worked examples and explanations that previously lived only in the repo are now served. (compiler/PYTHON.mdand the changelog are guide pages too.)The linalg-vs-NumPy comparison moved to the linalg page, beside the functions it measures (element-wise array math to the ndarray page). Each numeric function's Performance row now shows its own measured vs-NumPy number (µs/op + operand size + faster/slower), instead of a generic pointer.
Tighter prose. A pass over the guides trimmed wordiness without dropping facts, examples, or the single-threaded-by-design framing.
v0.7.0-alpha (2026-06-08) — cross-platform: Linux, macOS, and Windows
cheatah now builds and runs on Linux, macOS, and Windows. The language, the purrc interface, and every standard-library API are unchanged — this release is purely structural: the compile → link → load pipeline became platform-aware, so a .purr program compiles to the host's native loadable module and the runtime loads it, on each OS. (Linux is verified end-to-end; macOS and Windows use standard platform APIs behind detection and want on-device confirmation.)
Portability
One place for the differences — new
cmake/Portability.cmakedetects, per compiler/OS/arch: the native-arch flag (-march=native, falling back to-mcpu=nativeon Apple Silicon), the loadable-module extension (.so/.dylib/.dll), the vector-math library, and the flag/link listspurrcpasses the C++ backend. The rest of the build (andpurrc) just consumes them, so no#ifdefsprawl.purrcconsumes the baked flags; spawns the compiler viafork+execvp(POSIX) or_spawnvp(Windows); emits the platform module extension.Runtime validates the host's binary format — ELF (Linux), Mach-O incl. fat/universal (macOS), PE (Windows) — and loads via
dlopen(POSIX) orLoadLibrary(Windows). (Fixes "refusing to load … not an ELF shared object" on macOS, where a.sois really a Mach-O dylib.)Codegen exports
purr_mainthrough a portable macro (extern "C", plus__declspec(dllexport)so a Windows DLL exposes the entry point).stdlib —
socketgainsSO_NOSIGPIPE(macOS) and a Winsock backend (Windows);osgains the Windowsgetpid/setenvshims.
SIMD acceleration, per platform
Auto-vectorization on every platform via the detected native-arch flag (the bulk: products, factorizations,
sqrt).Vector transcendentals through the platform's vector libm where one ships: libmvec (Linux), Accelerate (macOS), opt-in SVML (Windows,
-DCHEATAH_WIN_SVML=ON); scalar-but-correct fallback otherwise.
v0.6.0-alpha (2026-06-08) — winning the numerics: world-class linear algebra + SIMD ufuncs
This release is a ground-up performance pass on the numeric core. We benchmarked every linalg and ndarray routine honestly against NumPy/LAPACK, hunted down why we lost where we lost, and fixed the causes. The result: cheatah now matches or beats NumPy on every dense linear-algebra routine measured at the small-to-moderate sizes most scientific code runs at — products, the LU family, the SVD (and its pinv/cond/ matrix_rank derivatives), and the symmetric eigensolver — and the element-wise math ufuncs now beat NumPy's too. The recurring villains were two: heap allocations hiding in element access, and inner loops that couldn't vectorize.
Linear algebra — algorithms and kernels
Killed the per-element heap allocation in matrix/vector extraction. The extractors read elements through
a.at({i, j}), and the{i, j}braced index heap-allocated astd::vectorper element — pulling out an n×n matrix did n² allocations before any math. Replaced with direct contiguous reads (memcpyfast path; strided walk for views). This alone is a large speedup across every routine.Zero-copy reads for the read-only routines.
dot/matmul/outer/trace/norm/cholesky/kron/conj_transpose(real and complex) now operate straight on the array's own buffer when it's contiguous — they allocate only their result.dotbeats BLASddot. The reduction was a serial floating-point dependency chain that can't vectorize without-ffast-math; rewritten with independent accumulators so-O3 -march=nativeissues SIMD+FMA. 16384-elementdot: 280µs → 3.7µs, from 36× slower than NumPy to 2.1× faster.invwins. It wasnserial-reduction back-substitutions (un-vectorizable); rewritten as a whole-identity block solve whose inner loops are vectorizable SAXPYs. 32×32: 26µs → 5.7µs, from NumPy-1.1× to cheatah 4.2×. (detalready won — same LU, but its SAXPY update vectorizes; that contrast was the tell.)Symmetric eigensolver: cyclic Jacobi → Householder tridiagonalization + implicit-shift QL (the method LAPACK uses).
eigvalshwent from losing 10–35× to tying LAPACK from 16×16 up, winning decisively below; it also skips the eigenvector accumulation it used to compute and throw away. The complex-Hermitian path rides the same solver via the 2n real embedding.SVD: one-sided Jacobi → Golub–Reinsch (Householder bidiagonalization + implicit-shift QR), reimplemented entirely column-major so the bidiagonalization reflectors and the QR's whole-column U/V rotations vectorize. The full decomposition now beats NumPy 1.7–1.8× (so
pinvwins 1.3–1.4×); a new values-only path ties LAPACK, and so docond/matrix_rank. Replacing the correctly-roundedstd::hypotin the O(n²) Givens rotations with the faster EISPACKpythagwas the final unlock. 64×64svdwent from 3505µs (NumPy 18.8×) to a 1.8× win.New
linalg.svdvals(a)— singular values only (≈numpy.linalg.svd(compute_uv=False)): skips U/V accumulation and the dominant U/V rotations.Fewer allocations everywhere: hoisted per-iteration working buffers out of loops (
inv,qr, the general eigensolver), factor-the-complex-LU-once in inverse iteration, single-copy hand-off into the eigen solvers.
ndarray — element-wise math beats NumPy
SIMD transcendentals via libmvec.
ndarray.exp/sin/cos/log/tan/sqrt/cbrtfor contiguousdoublearrays route through an isolated kernel TU (ufunc_simd.cpp) compiled-fveclib=libmvec -fno-math-errno, so they vectorize through glibc's vector math — without-ffast-math, so results stay strictly IEEE and the rest of cheatah's arithmetic is untouched.expnow wins ≈3×,sin≈5× at 16384 elements.array ⊕ scalarbroadcasting fast path. It was doing a bounds-checkedat()per element (no SIMD); a contiguous fast path tookndarray.addfrom ≈20× slower than NumPy to ~even, speeding up every scalar-broadcast op.purrcnow passes-fno-math-errno(letssqrt/algebraic math vectorize; strictly IEEE, unlike-ffast-math) and links-lmfor the libmvec symbols.
Docs, benchmarks & tooling
Honest, comprehensive vs-NumPy comparison on the performance page: ~25 routines across dimensions, full operand shapes stated (a 2×2 matrix, a 16384-element vector, n×n ⊗ n×n), green/red speedup styling, and the NumPy version compared against.
scripts/numpy_compare.pyexpanded to the whole library and made fair (full-SVD vs full-SVD, values-vs-values); newscripts/linalg_sweep.pyfinds per-function crossovers.Doc accuracy pass:
@allocrows now match the code (zero-copy where it is), algorithm names updated (Golub–Reinsch, tridiagonal QL), test names highlighted in the rendered tables.
Fixes
Corrected stale
@alloc/behavior annotations found in an audit (e.g.dot's "2-D matmul" comment — it rejects non-vector 2-D input; complexdot/vdotscratch claims).
v0.5.0-alpha (2026-06-08) — performance, honestly: @perf everywhere, vs-CPython & vs-NumPy, and a smarter editor
This release is about measuring cheatah honestly and surfacing those numbers where you work. Every standard-library function now carries a measured Performance row; the benchmarks compare against both interpreted CPython and NumPy/LAPACK (and report where cheatah loses, not just where it wins); the numeric core gains element-wise math; and the VS Code extension becomes a real reference tool.
Performance & benchmarks
@perfon every function. The reference docs and editor hover now show a measured Performance row per function — cheatah ns/call vs the honest baseline (CPython, or NumPy for the numeric modules) with the version it was measured against. Numbers live in one provenance-taggeddocs/perf_data.json, regenerated periodically byscripts/perf_suite.py(NOT in the QA gate — benchmarks are noisy/machine-specific).Honest benchmark methodology. Benchmarks are elision-proof (vary input + accumulate + print, so the optimizer can't delete the work — a naive loop measured a bogus "125000×"). cheatah is ~20–35× faster than CPython on real loops, ~1× where the work is already native (
hashlib), and the whole-program suite (Mandelbrot, N-body, RK4, integral) runs 14–97× faster.cheatah vs NumPy, by dimension. cheatah wins small/medium dense
matmul/solve/det/invand smalleigvalsh(the few-level-Hamiltonian physics case) by avoiding Python/dispatch overhead; NumPy's BLAS/LAPACK win at scale and ondot/largeeigvalsh. Reported both ways. (Large-dimension speedups are the future cheatah-gpu story.)No garbage collector — memory safety is RAII scopes +
shared_ptrrefcounting, so there are no GC pauses; documented on the performance page.The benchmark harnesses (
app_compare,perf_compare,numpy_compare) are themselves rewritten in pure cheatah (dogfooding); the QA gate stays in trusted tooling.
Numeric core (
Element-wise math ufuncs —
sqrt/cbrt/exp/log/sin/cos/tan/absover a whole array (the array forms of the scalarmathmodule; ≈ NumPy ufuncs), SIMD-vectorized on the contiguous fast path.
VS Code extension
Richer hover — each function shows a divided facts block: Performance (@perf), Complexity, Allocation, and the tests that cover it, with icons.
Go to Definition (Ctrl-click) on a stdlib call or an imported module opens its C++ header — resolved against the cheatah runtime you pick (
cheatah.rootsetting), the workspace, or headers bundled with the extension (kept in sync with the built runtime).User structs & interfaces — hover a type for its definition (fields/methods/ interfaces), a method/field for its doc; Ctrl-click jumps to the declaration.
Module names are colored, and the QA gate now auto-reinstalls the extension from the freshly-built runtime, so the editor never drifts (it had been stuck on v0.2.0). C++ IntelliSense for the benchmark sources fixed.
Docs
The performance page leads with the three benchmark comparisons grouped together.
Audited every doc for accuracy and fixed broken/again-runnable examples across the guides and module READMEs.
v0.4.0-alpha (2026-06-08) — complex linear algebra
cheatah becomes a tool for complex linear algebra — the kind physics (quantum, plasma) and signal processing actually need. The numeric core can now store and operate on complex numbers, the linalg module gains complex inner-product spaces and Hermitian eigensolvers, and a real matrix finally yields the complex eigenvalues it mathematically has instead of throwing. The docs site grows three new guides (Getting Started, Coming from Python, Security).
Numeric core (
Complex element type. The array element constraint widened from
NumerictoField— a real arithmetic type or astd::complexof a floating type — so complex matrices and vectors are first-class. Complex arrays print Python-style (a+bj/a-bj).Construct & inspect complex arrays:
complex(re, im),real(a),imag(a),conj(a).io.printrenders complex scalars Python-style too (8+3j, not(8,3)).
Linear algebra (
Complex spectra.
eig/eigvalson a general real matrix now return the complex eigenvalues (a rotation gives ±i) and complex eigenvectors (inverse iteration) — they no longer throw on a complex conjugate pair.eigh/eigvalshreturn the guaranteed-real spectrum (the numpy split).Complex Hermitian eigensolver.
eigh/eigvalshaccept a complex Hermitian matrix → real eigenvalues, complex eigenvectors (the quantum-mechanics workhorse), via a real symmetric 2n embedding.Complex inner-product spaces: complex
dot(bilinear) andvdot(conjugate-linear Hermitian inner product), complexmatmul, andconj_transpose(the Hermitian adjoint Aᴴ).
Docs
New site guides: Getting Started (with
.purr→.socompile + static/dynamic link diagrams), Coming from Python (porting guide), and Security (built-in protections vs. what you still own).Syntax highlighting in the generated site's code blocks — a lightweight, theme-matched highlighter that reuses the compiler's own keyword set.
Fixed code-block rendering in the generated site (spaces inside
<pre>were being collapsed).compiler/PYTHON.mdnow documents struct methods and interfaces, not just data classes.
Not yet
Templating the real decomposition routines over a generic
FloatingPointelement type (float32) is deferred to a follow-up — float arrays aren't constructible from cheatah source yet.
v0.3.0-alpha (2026-06-07) — a real language: methods, interfaces, generic numerics, and IntelliSense
Third pre-alpha, and the largest yet. This release turns cheatah from a small scripting core into a statically-typed, concept-driven language: structs gain methods and interfaces (lowered to C++ concepts), the numeric core becomes generic over its element type, and the editor gets full IntelliSense. A standing rule lands too — every template is concept-constrained, so misuse yields a named error, never template spam.
Language
Control flow:
break,continue,elif, andmatch/case.Collections: growable lists (
xs.append(v)/append(xs, v)), index assignment (d[k] = v,xs[i] = v), and empty typed declarations (let xs: list[int] = []).Slicing & indexing:
a[i:j](anda[i:],a[:j]), negative indices, and Python-style string indexing (s[i]is a length-1 string, sos[i] == "<").Method-call syntax:
obj.method(...)(UFCS) — including string predicatess.startswith/endswith/contains.Struct methods: declare
fn method(self, …)in a struct body → a real C++ member function (selfis implicit; non-mutating methods are emittedconst).Interfaces:
interface Shape { fn area(self) … }lowers to a C++20 concept;struct Circle : Shape { … }adds a compile-timestatic_assert(a struct that doesn't fulfill it fails with "Circle must fulfill Shape", not a template dump); andfn describe(s: Shape)becomes a concept-constrainedauto— static polymorphism, no inheritance, no vtables.
Numerics — generic over the element type
ndarrayis nowbasic_ndarray<T>over anyNumericelement type, deduced from the literals (array([1,2,3])is integer,array([1.0,…])is double).NDArrayremains the defaultbasic_ndarray<double>, so existing code is unchanged.Declarative SIMD: element-wise ops use
std::transform(std::execution::unseq, …)andsumusesstd::reduce(unseq)— vectorized for anyT— with a correct C-order fallback for broadcast/strided views. The linalg SIMD model (pure auto-vectorization, and the no-SIMD behavior) is now documented insimd.hpp.A
Numeric/FloatingPointconcept split is in place for the linalg generalization to come.
io
Printableprotocol:io.print/io.strnow render lists, dicts, structs, and ndarrays — a value is printable if it streams, exposes astr()method, or is a container of printables (recursive).NDArraygained astr().
Performance
Automatic string-concatenation optimization: the compiler rewrites
x = x + a + binto in-place appends (x += a; x += b), turning O(n²) string-building into O(n) with no full-length temporaries — an ease-of-development guarantee, no manual+=/builder needed.A new Performance docs page documents the compile-time-for-run-time bargain.
Compiler & policy
Constrain-all-templates: every emitted function/method parameter is a concept-constrained
auto(the baselineValue), and every library template carries a concept (Numeric,Ordered,Printable,Sized, …). No unconstrained templates anywhere — comprehensible compile errors by construction.
Tooling
VS Code extension → IntelliSense. Hover any stdlib/builtin function for its signature, params, and docs; type
module.for autocomplete. Backed by a generatedfunctions.json(built from the Doxygen XML), plus a "Get Started" walkthrough. The extension is now versioned with the language.QA gate enforces the extension stays in sync — a new hard-gate stage regenerates the extension hover DB from the stdlib API and fails the push if it drifted, so the editor never ships stale relative to the library.
The docs generator now renders hand-written guide pages (e.g. Performance).
Quality
100% unit-test line + function coverage and 100% Javadoc maintained across all the new code, under the existing ASan + UBSan + Valgrind QA gate.
v0.2.0-alpha (2026-06-07) — networking, a bespoke docs site, and a three-tier test system
Second pre-alpha. The language core is unchanged; this release adds networking to the standard library, replaces the documentation pipeline with our own site generator, and builds out a three-tier, per-function test system behind a stricter QA gate.
Standard library
New
socketmodule — a thin, memory-safe BSD-socket wrapper (tcp_listen/tcp_connect/accept/send/sendall/recv/bind/listen/connect/close/local_port/last_error, …).import socket.A pure-cheatah docs server (
scripts/serve-docs.purr) written entirely in.purron top ofsocket— nocpp { }, no raw pointers — that serves the generated site over HTTP. Proof that real programs can be written in cheatah.
Documentation
Bespoke documentation site. Doxygen is now used only as the C++ parser (it emits XML); our own generator (
docs/gen/generate.py) renders a modern static site — left module sidebar, client-side symbol search, a source browser, a light/dark toggle, cache-busted assets, and accessible (WCAG 2.1 AA) contrast. Replaces doxygen-awesome entirely.Structured doc tags. The old combined
@noteis split into@complexity(Big-O) and@alloc(heap behavior); every function also links three test kinds —@test(unit),@crtest(compile-run),@systest(system) — straight to their source. 100% Javadoc coverage of the public stdlib, plus a behavioral description for ~190 functions.
Testing
Three-tier, per-function tests: a C++ unit test and a compile-run test (compile a
.purrcalling the function, run it on the runtime, assert exact stdout) for every function; a comprehensive per-module system test that exercises every function of its module; and six cross-module system apps (GradeReport, LinearSolve, EventLog, Integrity, MonteCarlo, NetworkRoundtrip) that only pass if many modules cooperate.
Quality & security
The QA gate now hard-fails below 100% unit-test line+function coverage and below 100% Javadoc coverage. ASan/UBSan + Valgrind run across all test tiers.
The ~200-test per-function compile-run battery is opt-in (
QA_GATE_FULL_CR=1) so the default gate stays fast.On a passing push to
main, the docs site is auto-regenerated.
v0.1.0-prealpha (2026-06-06) — first pre-alpha
The first tagged pre-alpha of the cheatah language: it compiles and runs, with a standard library, an editor extension, and CI that runs every test under two memory checkers.
Language
Python-like surface, C-style
{ }blocks, compiles to native code viapurrc(lexer → parser → codegen → C++), run by the headlesscheatahruntime.letvariables;int/float/str/bool; full operators incl.**(power);if/else if/else,while,for … in range(…);fnfunctions (recursion);structrecords;list/dict/arraycollections;try/except+raise;importwithasaliases and dotted modules.cpp { … }raw-C++ escape hatch — file scope at the top level, inline inside a function (memory safety is the author's responsibility there).;is an optional statement separator/terminator (and struct-field separator).
Standard library
builtins, io, os, string, math, time, datetime, random, statistics, hashlib, and a SIMD numeric core (ndarray + numpy-style linalg). Each module builds as both a static and shared library.
Tooling
VS Code extension (editors/vscode/): syntax highlighting (with embedded C++ in
cpp { … }) and a "Seti + cheetah" file icon theme.purrc --version/cheatah --version.
Quality & security
100+ tests (unit + purrc→runtime end-to-end). QA gate (pre-push hook) runs them under ASan + UBSan and Valgrind, plus release benchmarks.
Security review + hardening (ndarray overflow/OOB guards); threat model and the Unix-interface/MCP safe-design plan in SECURITY.md.
Known limitations
Single-trust model — do not run untrusted
.purryet (no sandbox).No Unix system-call interface or MCP server yet (designed, not built).
Native GitHub
.purrhighlighting pending a github-linguist submission.
