Skip to content

Releases: Chelis-Lang/chelis

v0.19.1

Choose a tag to compare

@github-actions github-actions released this 06 Oct 19:53
5105ce1

[0.19.1] - 2026-10-06

Fixed

  • Public Chelis toolchains and Reef packages install from GitHub releases without
    a GitHub token. Private releases continue to use GITHUB_TOKEN or gh auth token.
    See #2840.

  • The checker processes nested aggregate result constraints together, avoiding repeated traversal when checking long list literals. See #2975.

  • Reef builds with an exact package pin no longer fail because an excluded version in the local registry has an unreadable shell format. Errors for an unreadable matching candidate name its version and matching requirement. See #3290.

  • chelis tide mcp uses newline-delimited JSON instead of LSP-style
    Content-Length headers, so standard MCP clients can initialize, discover tools,
    and call them over stdio. Notifications no longer receive spurious error
    responses, and the server answers ping requests.

  • The release E2E softmax probe states its tensor dtype explicitly, so it remains
    valid under the tensor literal ingress contract.

v0.19.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 13:22
05f52ce

[0.19.0] - 2026-10-06

Changed

  • BREAKING: Keep Surf pipes as syntax sugar; fmt preserves authored pipes, while deep and surf emit normalized calls. Ungrouped mixes with other operators, postfix access, ascriptions, updates, or open-ended forms now require parentheses. Previous Deep/cache/package payloads must be regenerated.

    Silent numeric value change: 0.1 |> cast(f64) now adopts the f64 cast target before rounding, exactly like cast(0.1, f64). Its bits change from 3fb99999a0000000 to 3fb999999999999a. To retain the former value, write 0.1f32 |> cast(f64). Before upgrading a shell or downstream repository, run chelis migrate pipes --baseline-compiler OLD --inplace PATH...; it proves the complete expanded Deep unchanged before replacing the batch. --check audits without writing; explicit --keep-going checks or migrates independently proven files and reports every failure with a nonzero exit.

  • BREAKING: A numeric literal's dtype is written at its site: it comes from the literal's suffix, or from a dtype-stating construct that directly contains it when the literal's kind admits the stated dtype, or else from the i32/f32 default, and never from a callee's signature or anything further away. The closed set of constructs is a declaration (the declared type of the binding or def result that the literal, or its negation, initializes, and a declared tensor type's element dtype for every literal element of a bare bracket initializer), a cast whose operand is the literal, and a new optional dtype argument of to_tensor. Expression ascriptions such as (1.0 : f64) and declared List types still only check.

    • Scalar declarations now bind their literal: x: f64 = 1.1 is exactly 1.1f64, def f() -> i8 = -128 binds the i8 minimum, and x: i64 = 3000000000 is accepted. Previously each was rejected as a dtype mismatch. A declaration states the dtype only of a literal that is its whole initializer: x: f64 = neg(1.1) and x: f64 = if c then 1.1 else 2.2 stay rejected, and so do type aliases such as x: P = 1.1.
    • to_tensor(xs, p) takes an optional dtype p, a primitive in an argument position that always names a dtype, never a value. It states p for every unsuffixed literal element of a bracket-literal xs, so to_tensor([1.1, 2.2], f64) builds the exact f64 values. It never converts: a leaf dtype other than p, such as to_tensor([1.0f64], f32), is a type error. A dtype-bounded binder p is accepted as the dtype argument over leaves that already have dtype p, such as to_tensor([cast(1.1, p)], p). An unsuffixed literal that a binder dtype argument or a declared binder type would bind, as in to_tensor([1.1], p) or def f[p: Float](x: p) -> p = 1.1, is rejected with a diagnostic that names cast(1.1, p); see #3148.
    • An unsuffixed literal element of a to_tensor call without a dtype argument is a type error. Write to_tensor([1.1, 2.2], f32) (or i32 for integers) to keep the previous value, or state the dtype you mean. In particular cast(to_tensor([1.1, 2.2]), f64), which rounded both elements through f32, is rejected and its diagnostic names both to_tensor([1.1, 2.2], f64) and cast(to_tensor([1.1, 2.2], f32), f64) (#3080). xs: tensor[2, f32] = to_tensor([1.1, 2.2]) is the same error; the bare bracket xs: tensor[2, f32] = [1.1, 2.2] keeps taking the declared dtype. A List bound first keeps its defaults: xs = [1.1]; to_tensor(xs) is accepted. In Deep, an untyped numeric literal element of a to_tensor argument is rejected.
    • A cast whose unsuffixed literal operand its primitive target binds desugars to that literal alone, with no cast node: cast(1.1, f64) has exactly the Deep of 1.1f64, and cast(3000000000, i64) that of 3000000000i64 (#3135). A negated operand folds into one signed literal at the target, so cast(-128, i8) is the i8 minimum, which has no suffixed spelling. Values are unchanged. Every other literal cast keeps its node and converts: cast(1.1f32, f64) still widens the f32 value, cast(2.0, i32) converts the f32 default, cast(1, bool) is true, and a cast to a dtype binder converts at the binder's members the literal's kind does not admit.
    • to_tensor is a reserved name. Binding it as a top-level or package def, sig, or macro, a function, lambda, or macro parameter, a block or pattern binder, or an imported name is rejected as ReservedName in Surf and in Deep, inside a reef package too. This replaces the refusal of a declared tensor literal under a local to_tensor binding, and it rejects the previously accepted programs of #3152 and #3167.
    • chelis surf prints a literal without its suffix only where re-reading the output binds it at the same dtype, and always suffixes a cast's literal operand, so a Deep-to-Surf round trip keeps every literal's dtype and every cast node (#3165).

    See #3164 and #3136.

  • BREAKING: A macro definition must reference every one of its parameters in its body. chelis check, eval, and build now reject a macro whose body never uses a parameter, naming the macro and the parameter, whether or not the program calls the macro; a reference inside the scope of a body binder of the same name, such as a fn parameter, does not count. Previously the argument for such a parameter was dropped during expansion, before name resolution, type checking, effect inference, and linearity checking, so macro first(a, b) = a with first(1i32, never_declared), first(1i32, print("boom")), or a reused key checked clean and ran without the argument's effect, and a wrong-arity macro call inside the argument was never rejected. Rewrite such a macro without the unused parameter.

    In a single file or an entry module, a macro parameter now shadows an imported name of the same spelling inside the macro body, as it already did in library modules. Previously import Std.Scalar (abs) with macro apply(abs, x) = abs(x) bound the body's abs to the import, so apply(neg1, 5i32) called Std.Scalar.abs and dropped neg1. See #3266.

  • The canonical project reference describes Chelis's architecture and package
    boundaries in present tense. See PR #3274.

Fixed

  • Rank-polymorphic chelis eval calls now enforce checked local tensor extent claims in execution order, including claims after the returned value is produced. Local claim axes account for expanded rank spreads in both eval and compiled C. See #3092.

  • The checker rejects constructor and tuple patterns that cannot match the
    scrutinee type, including wrong nominal constructors, non-tuple scrutinees,
    and tuple arity mismatches. See #3228.

  • Compiled C programs now respect the scope of match-arm pattern binders when a call is inlined. A pattern that re-binds a parameter's name, in any constructor, record, tuple, @ or nested pattern, keeps that name for its guard and body, and a pattern binder no longer captures a name read by a function passed in or defined around the match. Previously such programs could print a different result from chelis eval. See #3229.

  • A positional pattern over a record now binds the record's declared fields in the tensor graph that grad and vmap lower to, whatever order the record literal wrote its fields in. Previously match P { b: ..., w: ... } with { | P(w, b) => ... } bound w and b to each other's values there. As a result, grad returned the gradient of a different function, vmap returned wrong forward values in both chelis eval and compiled C, and compiled C returned a wrong gradient with respect to a record-typed parameter built from such a literal. Field expressions still evaluate in written order. A lowering that cannot determine a record's declared field order now rejects such a pattern instead of guessing. See #3230.

  • The checker rejects calls that use one type binder as both a tensor precision and an incompatible ordinary type. Cyclic precision constraints also report a type error. See #3233.

  • chelis prove no longer certifies a false property whose binder or module name is spelled like a solver symbol the prover generates, such as __erf_abs_0, __contract_std_normal_cdf_0, or __contract_std_quantile_0. Generated symbols are now chosen fresh against every name in the goal, so such a name stays an independent variable and the property is refuted or left unsupported; a counterexample may show the generated symbol with a numeric suffix. A solver goal that declares one variable twice is rejected instead of merging the two variables into one. See #3236.

  • A macro call must supply exactly one positional argument per macro parameter and no named argument, and a macro definition may not repeat a parameter name. chelis check, eval, and build now reject a call with too few or too many arguments, naming the macro and both counts; a call that passes accumulator=, naming the macro and the argument; and a repeated parameter, naming the macro and the parameter. Previously a missing argument let its parameter resolve to a same-named binding at the call site, a surplus argument was dropp...

Read more

v0.18.13

Choose a tag to compare

@github-actions github-actions released this 05 Oct 16:14
01ce8dd

[0.18.13] - 2026-10-05

Added

  • BREAKING: A type binder's dtype bound may now be an explicit set of dtypes, written [p: {f32, f64}], beside the existing Float, Int and Numeric families. A set admits exactly the dtypes it lists, so a declaration can exclude a member its family would admit — which is what lets a generic body hold a constant that one narrow family member cannot represent, such as a literal that rounds to infinity at f16 under Float. A family still denotes whatever the active dtype set admits into it and widens when a dtype is activated; a set never does, so a set enumerating a family's current members is not that family. Breaking: the package-shell envelope moves to format version 6 because a published scheme can now carry a set-valued type-variable domain, so shells built by earlier compilers must be rebuilt. See #2443.

  • Compiled C runs every host-runtime operation that chelis eval runs, with
    the same result or failure in the same effect order: tensor_scan over
    scalar states, process_run, clock_wall_read and clock_monotonic_read,
    round_to, the CSV builtins (parse_csv, to_csv, and the csv_*
    accessors), and the test_assert* assertions. Each runs through one
    definition in the runtime, shared by both lanes. A program that uses one of
    them, or only defines a function that does, no longer fails chelis build
    with an "eval/test lanes only" or host-only error, and an uncalled assertion
    compiles to working code rather than an abort stub. Tensor states for
    tensor_scan remain blocked on the checker
    (#2999). See
    #1297.

  • Std.Datetime.Business adds business-day calendars to chelis-std. A BusinessCalendar combines a Weekmask of business weekdays, a holiday list, and an inclusive horizon. It answers only from the days of that horizon, and a query whose answer depends on days outside it fails domain. The module provides is_business_day; business_day_roll with Unadjusted, Following, Preceding, ModifiedFollowing, and ModifiedPreceding; business_day_offset, which first rolls a non-business start under a required NonBusinessStart policy and then moves; half-open, antisymmetric business_day_count; and business_in_all and business_in_any for settlement and union calendars. The constructor, the queries, and the combinations each have a try_ form, and vectorized forms of the queries work over Dates[n] columns. A scalar query reads O(log h) holiday-list elements for h holidays, whatever the horizon's length. See #2860.

  • Std.Datetime.Columns adds vectorized forms of Std.Datetime over Dates[n]
    and Instants[n], and a Durations[n] column. They read date fields, build
    dates from fields, add days and months under a DayOverflow policy, compare
    columns, parse and print date text, convert unit counts under a TimeUnit and
    a Rounding, add and measure durations, round instants to an increment, read
    dates at an offset, and give an f64 time axis. Each callable agrees with its
    scalar twin at every element; a failing call names the lowest failing element,
    and the masked try_ forms return a validity mask instead of failing. See
    #2861.

  • Std.Datetime.Zone adds time zone rules as values. time_zone_from_tzif reads a TZif file (RFC 9636, versions 2 to 4) from bytes the program supplies, including its POSIX TZ footer rule; time_zone_fixed and time_zone_utc build fixed zones. Zoned values convert local readings with a required Disambiguation (EarlierInstant, LaterInstant, CompatibleInstant, RejectNonUniqueLocal) for daylight saving gaps and folds, add durations on the instant line and periods on the wall clock, and read and write RFC 9557 text, resolving a written offset with a required OffsetConflict policy. Text whose offset is Z, z or -00:00, which RFC 9557 reads as a UTC time with an unknown local offset, resolves to that UTC instant under every policy. A zone whose footer is empty fails domain from its last transition on instead of extrapolating. See #2862.

  • Std.Datetime.Clock reads the host clocks. clock_now() returns the wall-clock
    Instant, and monotonic_now() returns an opaque MonotonicInstant from a
    clock that never runs backwards. monotonic_until(a, b) is the exact Duration
    from a to b. It is the only datetime function that takes a
    MonotonicInstant, which does not convert to an Instant. Both reads carry IO, so a caller declared
    without IO cannot call them. A failed read reports
    clock_wall_read: io: <detail> or clock_monotonic_read: io: <detail>, naming
    the underlying builtin. The module is separate from Std.Datetime, so nothing
    in Std.Datetime reads a clock; any code that reads one carries IO. chelis eval,
    chelis test, and compiled C run the clocks
    (#1297).
    See #2863.

  • Std.Decimal adds decimal_to_f16 and decimal_to_bf16, which round a decimal's exact value once, to nearest with ties to even, directly to f16 or bf16, subnormal results included. Casting decimal_to_f32 to f16 or bf16 rounds twice and can differ: decimal("1.00048828125000001") converts to the f16 1.0009765625, while its f32 image is an exact f16 tie that the cast rounds to 1.0. A decimal of magnitude 65520 or more converts to the f16 infinity of its sign, and a negative decimal that rounds to zero converts to negative zero; neither conversion fails, and every decimal is finite in bf16. The module's laws (the text round trip, commutativity of decimal_add with decimal_sub inverting it, decimal_to_f64(decimal(t)) == to_float(t), idempotent decimal_round, and the i64 round trip) are stated as chelis prove properties in packages/chelis-std/properties/decimal.ch. spec/04 §1.1.1 now decides that exporting a Std.Decimal v to a declared decimal128 or decimal256 precision p and scale s fails domain when |round(v, s) · 10^s| >= 10^p, never saturating or truncating. See #2996, #2946, and #2920.

  • mod now accepts two floats of one dtype, scalars or same-shaped tensors, and computes C fmod, as [05-OP-64] specifies: the exact remainder with the quotient truncated toward zero, so the result has the dividend's sign. mod(x, 0), an infinite dividend and a NaN operand give NaN, and an infinite divisor returns the dividend. f16 and bf16 compute at f32 and narrow once, which is exact. chelis eval and compiled C agree. Differentiating through mod is refused at every dtype with a diagnostic that names the jump in its truncated quotient. See #626.

  • cast_saturate(x, T) and cast_wrap(x, T) complete the named lossy casts beside cast_trunc. cast_saturate takes a signed integer or float and returns a signed integer: a float is truncated toward zero, then the value is clamped to the target range, -inf and +inf give the minimum and maximum, and NaN traps Domain ([05-OP-23]). cast_wrap takes a signed integer and returns the target-width two's complement value without trapping ([05-OP-24]). Both work on scalars and on owned or borrowed tensors, including the x |> cast_saturate(i8) pipe stage, and refuse grad. chelis eval and compiled C agree on every admitted source and target pair, and compiled C now also converts cast_trunc over a tensor computed on the host instead of refusing it. HIP builds refuse all three named casts until device traps exist. cast_saturate and cast_wrap are now reserved words, and the serialized operation graph moves to schema 25, where named_cast with a mode replaces the cast_trunc operation. See #759.

  • chelis lane-check now compares the complete evaluator and compiled-C stdout of deterministic programs and reports divergences, failures, and provenance as versioned NDJSON. A locked native Linux x86-64 Nix check supplies the authoritative exact-output acceptance gate; local host-toolchain runs remain diagnostic. See #763.

  • A source-derived C support inventory distinguishes builtin exclusions from
    stdlib routes and partial capabilities, documents each rejection's whole-program
    or live-code scope, and retains executable refusal/admission witnesses
    (#1170).

  • Two host clock builtins, clock_wall_read() and clock_monotonic_read(),
    return (seconds, nanoseconds) as (i64, i64) from a single host reading,
    with nanoseconds in [0, 10^9). The wall clock is on the POSIX timescale;
    the monotonic clock never runs backwards and has an unspecified origin. Both
    carry the IO effect, so a function declared without IO cannot call them.
    A host clock error, or a reading whose seconds lie outside
    -377705030401..253402214400, fails with <builtin>: io: <detail>.
    chelis eval and chelis test run them through the evaluator's
    policy-checked system port, and compiled C runs the same reads
    (#1297). [05-OP-75]
    in spec/05-risc-primitives.md defines them.

  • Linux releases publish a static build, chelis-v<ver>-linux-x86_64-static.tar.gz,
    and the bootstrap chelisup-linux-x86_64 is static too. Both are static-pie
    executables that need no program interpreter or system library, so they...

Read more

v0.18.12

Choose a tag to compare

@github-actions github-actions released this 30 Sep 20:43
c81d818

[0.18.12] - 2026-09-30

Added

  • BREAKING: Reef now parses canonical package identities and prefers valid exact locks.
    Package versions use Semantic Versioning without build metadata. Manifest
    schema 1 keeps exact dependency semantics; schema 2 activates deterministic
    Cargo-style resolver-2 requirements. Noncanonical package names, partial
    versions, non-SemVer release tags, and malformed locks, which earlier releases
    accepted, now fail closed. See
    #1327.

  • BREAKING: The key element dtype and the explicit key operations key_from_seed, split_key, split_keys and fold_in ([05-OP-69] through [05-OP-72], derived under [05-RNG-2]) exist in the IR, the DAG evaluator and compiled C, and chelis build --target hip refuses them with its typed rejection (#2413, step 1). key is an active tensor element dtype, stored as runtime dtype key (id 9) in a chelis_tensor, with no arithmetic, comparison, cast, literal or default; DLPack and NumPy refuse key tensors. A draw may take a batch of keys, one per row of its data.

    BREAKING: WireDag adds the four key operations and admits key at any rank.

  • The compiler API evaluator now routes its seven filesystem builtins and
    process_run through one injected system boundary with independently checked
    Filesystem and Process permissions. Normal evaluation permits both, while
    invariant predicate revalidation refuses both before invoking the host
    adapter. The default adapter preserves existing filesystem, process, and
    error behavior, including byte-ordered directory
    names and strict UTF-8 failure under [05-HOST-4]. This evaluator-only change
    does not alter compiled binaries or the runtime ABI; compiled host
    process_run support remains required by [05-HOST-2] and tracked under
    chelis#1297. See
    #1323.

  • Reef documents now have independent versioned schemas. New manifests write
    schema 3 and new lockfiles write schema 1. Legacy files remain readable with an
    upgrade warning. chelis reef upgrade --check|--inplace provides preflighted
    ordered migration. Project writers use .reef-write.lock and atomic
    single-file replacement. Versioned JSON Schema files under docs/schemas/reef/
    provide advisory editor validation. See
    #1327.

  • Reef manifest schema 3 accepts typed package metadata: a description, an SPDX
    license or a custom license file, HTTPS repository, documentation, and homepage
    URLs, and a README. Declared files use portable paths, no-follow Unix opens,
    stable bounded snapshots, and deterministic archive membership. Metadata is
    not part of logical package identity or resolution, and its fields are not
    serialized into reef.lock, index.json, or .chb. Artifact integrity hashes
    still change when declared bytes or the manifest change.
    Prepared graph cache version 10 rejects earlier envelopes. See
    #1327.

  • Reef now performs bounded GitHub release discovery. Normal commands reuse a
    valid lock without listing releases. chelis reef update [<package>] performs
    a full or targeted refresh, and chelis reef outdated [<package>] [--json]
    reports available versions without final writes. Both commands reject index
    or remote artifact hashes that disagree with an existing lock, even when a
    cache directory or index entry is missing and the package is unrelated to a
    targeted refresh. An uncached pin occupies a candidate-manifest slot even
    when newer releases exceed the scan limit; an unavailable or mismatched pin
    fails closed. Candidate scans, requests, downloads, and resolver states
    have finite limits, and Reef publishes complete verified cache entries before
    it replaces reef.lock. An explicit chelis-std
    dependency uses the compiler bundle without network access. Cyclic path
    dependency graphs fail with an ordered cycle report before resolution. See
    #1327.

  • chelisup install now checks a release's runtime files before placing it. It
    runs the unpacked chelis runtime export and refuses the release unless the
    exported archive and each receipt-listed header are regular non-symlink files
    whose SHA-256 matches the receipt, and the shipped archive and headers match
    those same digests. A refused release leaves the store untouched. Releases up
    to 0.18.11 predate the export and install unchecked, with a warning. A refused
    release newer than the running chelisup also says how to get the latest
    chelisup, and a release whose chelis the operating system refuses to execute
    is reported as such. See #1354.

  • chelis runtime export <dir> writes the runtime archive and public runtime
    headers that the chelis binary carries, with a receipt recording the archive's
    SHA-256. Release tarballs and the Nix chelis package ship its output, so their
    lib/libchelis_runtime.a is the archive chelis build stages. See
    #1354.

  • A core-fragment eval/C parity receipt runs a pinned corpus of programs through
    both chelis eval and the compiled C lane and compares their complete
    observations byte for byte, and their traps by exit status and complete
    diagnostic. The corpus is a versioned manifest that records every case's
    expected outcome and every excluded file's reason, so a run cannot pass
    vacuously. It is a manual gate, documented in
    docs/manual_gates.md; its contract is
    spec/design/core_fragment_parity_corpus.md.
    See #2102.

  • chelis reef conform audit recognizes Coral and Nautilus thin callers of the known historical central workflow revision with closed profile inputs and secret mapping. Structurally parsed YAML jobs and events make alternate indentation, quoted keys (including on), and aliases obey one authority; duplicate keys fail closed, while comments and run strings remain inert. GitHub owner/repository case differences cannot hide mutable, unknown, or alternate-path central pointers. Each historical CI, nightly, or release profile requires the raw reef compiler, package, and Nautilus sources its actual grep-based jobs read, even when a release caller omits pin inputs; these must agree with parsed TOML and supplied caller pins. The first grep-extractable numeric version on a ^nautilus line is decisive, even when an adjacent dependency matches the prefix; later lines do not override it. Valid inline Nautilus trailing fields/comments remain readable, while separate tables alone do not certify the 439 guard. The committed Linux/Darwin toolchain digest lock must match. CI callers must run for main pushes and pull requests to certify the guard and test suites. This audit does not approve a consumer migration, a newer central revision, or hosted execution.

  • Add a bounded keyed-randomness acceptance command with exact test receipts and
    explicit compiler, par rejection, and shell-release coverage boundaries.

  • Downstream shells may add an optional Nix verification job that rebuilds the
    shell with atoll's chelis2nix and requires the bytes of the shell's chelisup
    build. spec/design/shell_repo_contract.md §2 states its conditions: the
    chelisup lane stays the gate, the job runs only on pushes to main, and it
    takes the compiler only by substitution and never builds it.

Changed

  • BREAKING: Randomness now uses explicit keys instead of the Random effect (#2413). A key is a non-numeric dtype. key_from_seed(seed) makes a root key; split_key(k) returns two keys, split_keys(k, n) returns a tensor[n, key], whose extent follows expand's rule (a literal n is that literal extent and any other n a runtime extent, checked where it meets another), and fold_in(k, n) derives one key from an integer. The draws take the key first: dropout(k, x, rate) and uniform_like(k, t, low, high); a call at the old keyless arity names the retired spelling. The with seed(N) { ... } handler, the Random effect name in Surf effect annotations, and the random kind of Deep's handle-effect are removed; each is now a typed rejection that points at keys. A function that draws takes a key parameter instead.

    Keys are affine ([04-LIN-9]): each is used at most once on every path and cannot be copied, borrowed or captured by a closure, and reuse is a KeyReuse error suggesting split_key or split_keys. An as-pattern cannot bind a key-carrying value together with a key-carrying component of it, and a key a match guard consumes stays consumed in every later arm, because a failed guard falls through. filter, partition, scan and tensor_scan refuse a key-carrying element or accumulator, which would reach both their callback and their result, while map, fold and the other list builtins move keys; tensor concat and split refuse key tensors; and vmap refuses a key-carrying argument it would share across rows. A key-carrying value reaches only the operations the key allow-list names: a key derivation or draw at its key operand, drop, a branch's join, construction and destructuring, a move, a list builtin that routes each value to one consumer, and a call through a key-typed parameter, grad, vmap and jit applications included; reading a key tensor's extent with shape or numel (as an expand, insert or reshape size too), or at an extent check (of a call whose signature shares a dimension between a key tensor and other...

Read more

v0.18.11

Choose a tag to compare

@github-actions github-actions released this 21 Sep 23:38
a7e592f

[0.18.11] - 2026-09-21

Added

  • HIP now executes direct tensor comparisons, Bool8 logical operations, and
    stored-bit where selection across all active device dtypes. Metal reports a
    stable typed unsupported diagnostic for these operations until its exact
    kernels land. See #1284.

  • The diagonal and trace axis rules now have expectations that neither runtime
    computes. A new CLI suite derives extents and elements from [05-OP-33] and
    requires the IR evaluator lane and the compiled C lane to agree with those
    expectations separately, over all six ordered rank-3 axis pairs, both rank-2
    orders on a non-square matrix, and a signed source. The parity harness header
    now states what lane agreement does and does not establish. See
    #1351.

  • The Deep canonical form specification states the order of annotation metadata map entries. Entries are ascending by key spelling under ASCII byte comparison, and producer-specific and span_* extension keys take their places in that one sequence beside the defined keys. The order was already observable in canonical Deep output and was the one member missing from the chapter's ordering enumeration. See #2214.

  • python3 scripts/gate.py --fast now classifies the changed path set through
    the change-owned planner's own rule lookup and refuses a push that adds a file
    no [[path_rule]] in .config/ci-test-targets.toml routes. The planner
    itself cannot run locally, because in pull_request mode it requires a
    two-parent synthetic merge, so until now a new tracked file passed every local
    check and then failed Plan Changed Integration Tests in CI. The check prints
    the same unclassified changed path: <path> sentence CI prints, from one
    shared spelling, and reports every unrouted path rather than stopping at the
    first as CI does.

    It derives its own changed set when it runs, so a modified artifact is
    classified in the run that changed it. A path is classified once git knows
    about it, so a file a writer has just created is seen at the next --fast
    after git add rather than in the run that created it; an unadded file is
    not part of the change and cannot be pushed. It matches the
    planner's rename handling, classifying both sides of a move rather than only
    the destination, and it ignores untracked work, which CI never sees and
    nothing can route. It is not a proof that CI will agree: it reads the working
    tree, while CI composes base and candidate Cargo metadata inside the synthetic
    merge, so a change to the package set itself can be ambiguous to the planner
    and clean locally.

    Four paths that no rule routed are now routed, three of them generated
    artifacts whose only consumer runs nightly. ci_change_owned.py classify-paths is available on its own, with --from-git for the derived
    set.

  • Downstream shells can omit an entire shared agent skill with
    conform.excluded_skills, or remove exact inherited sections from a retained
    skill with shell-local:exclude heading selectors. Sync and version bumps
    reapply both forms of exclusion while preserving shell-specific additions.

  • Add a repeatable PR lifecycle report that inventories every attributable
    GitHub Actions run and attempt, estimates per-job-rounded Linux list price by
    runner SKU, rejects stale or identity-rewriting attribution, and separates
    latest-candidate validation, cumulative execution, semantic causes, and
    temporally bound trace-attributed agent waiting.

Changed

  • BREAKING: Chelis source and canonical Deep now spell signed integer dtypes i8, i16,
    i32, and i64. The retired int8, int16, int32, and int64 language
    spellings are rejected with migration guidance. Run
    chelis migrate surf --from 0.18 --inplace for .ch source and
    chelis migrate deep --from 0.18 --inplace for .dp source. Existing
    compiler-API JSON, WireDag, cache, and C ABI dtype names remain unchanged.
    See #1592.

  • BREAKING: chelis_types::Scheme gains a public constraints: Vec<CollectionConstraint> field, so direct struct literals must supply it; Scheme::mono is unchanged. TYPE_ENV_FORMAT_VERSION advances from #2071's version 2 to version 3. Older checker snapshots are rejected instead of silently restoring checked collection function values without their contracts. See #1654.

  • BREAKING: Published package metadata now records checked collection relations. chelis_shell::ShellSymbol gains collection_obligations: Vec<CollectionObligation>, CHB advances from #2071's format 4 to format 5, and reef schema advances from format 2 to format 3. Each relation names len, index, append, or concat and carries exact canonical Deep operand/result types under variables already present in the exported callable type; ledgers are strictly ordered and unique, and hidden variables are rejected. CHB validation and encoding/decoding plus public Reef-schema deserialization reject parse-equivalent noncanonical strings, duplicate or out-of-order rows, predecessor versions, and unknown versions. CHB and Reef schema protect publication identity; serialized TypeEnv owns compiler checker reuse. See #1654.

  • BREAKING: Function signatures now quantify type, dimension, and rank variables only
    from explicit [...] binder lists. Add every formerly implicit name to the
    owning sig or def; a standalone sig owns the declaration's sole list.
    Canonical polymorphic Deep inserts a structural (binders...) child between
    the defsig name and type, while monomorphic defsig remains two-child.
    Binder lists reject active, reserved, retired, and deferred dtype spellings
    even when unused or used as dimension/rank variables. Unknown dtype-like
    names are rejected with the nearest active spelling and one diagnostic per
    declaration/name.

  • BREAKING: The List slice drop(xs, n) is now skip(xs, n). drop keeps only its
    one-argument form, the linearity consume that pairs with copy(). The two were
    one builtin declaration separated by argument count, so a dropped argument
    turned one operation into the other and still type-checked; they are now two
    declarations governed by two atoms, [05-OP-54] for the slice and a new
    [05-OP-67] for the consume. skip pairs with take, which already had the
    matching semantics.

    Migrate existing sources with chelis migrate surf --from 0.18 --inplace. The
    rewrite is arity-driven and scope-aware: it renames drop(xs, n) and the pipe
    stage xs |> drop(n), and leaves drop(value), xs |> drop, and any drop
    shadowed by a parameter, lambda parameter, block binding, or match binder
    untouched.

    Two further consequences for existing code. skip joins the closed builtin
    vocabulary, so a top-level def skip is now rejected as BuiltinShadowing
    (spec/04 section 8.6); rename that definition or scope it inside a Reef
    package. The standard library's Std.Index wrapper drop_list is now
    skip_list, with the same signature and semantics.

    chelis lint gains the advisory rule recursive-list-cursor
    (spec/01 section 12.3). It reports a self-recursive definition whose recursive
    call passes skip(p, k) in the argument position its own parameter p
    occupies: that walk copies the List once per step and costs time quadratic in
    its length. Std.Tokenizer's three JSON entry builders had exactly that shape
    and now run on fold and map.

  • chelis lint's advisory recursive-list-cursor now also reports a cursor the
    recursive call reaches through a local binding, as in rest = skip(xs, 1i64)
    followed by walk(rest, ...). Previously it read only a skip written in the
    argument position itself, which missed every cursor found in the shell
    ecosystem. Substitution is one level deep and respects binding order, and a
    name a definition binds more than once is still never substituted. A cursor
    reaching the argument through a call to another function remains outside the
    rule permanently: whether that call returns a suffix of its argument is
    interprocedural. spec/01-nomenclature.md §12.3 states the new scope. See
    #2328.

  • chelis build no longer formats an ownership-verification diagnostic that the
    success path discards, and no longer desugars a single-file program twice. The
    first helps every build; the second helps only a build outside a reef package,
    because a package build's second pass already covers just the entry module. The
    emitted artifacts are unchanged, and the live-owner overflow diagnostic is
    byte-identical on the path that raises it. See
    #2331.

  • skip(xs, n) on a List the compiler proved is at its last use now advances an offset inside the existing allocation instead of cloning the retained suffix. A recursive cursor that binds its head before recursing therefore runs in linear rather than quadratic time; one that reads the head inside a later argument of the same call keeps its operand live and stays on the cloning path, as does a List any other owner still holds.

  • Pull request CI now publishes default-branch-verified candidate receipts that
    bind the exact head, synthetic merge, patch identity, and required-check
    provenance. These receipts provide trusted input for later evidence reuse;
    pull-request code cannot issue or validate its own receipt.

  • Change-owned pull-request tests are now assigned to four shards using reviewed target-duration evidence, preventing a hash collision of slow targets from overloading one runner without increasing the job count or changing test ...

Read more

v0.18.10

Choose a tag to compare

@github-actions github-actions released this 15 Sep 12:40
b9095cc

[0.18.10] - 2026-09-15

Changed

  • BREAKING: Generic function bodies must satisfy their declared dtype-family bounds: an
    unbounded or Numeric binder cannot use a float-only operation without a
    sufficient Float contract. Omitting a signature does not publish an inferred
    generic admission contract; local inference holes must bind within their
    enclosing declaration or be justified by its declared bounds. Checked
    operation-family restrictions now survive function values and transitive
    calls. The specialized reduce_window_* shape path consumes those same
    contracts: reduce_window_mean requires Float, while window sum, max and min
    require Numeric. Older checked-context caches and package shells must be
    rebuilt.

  • Move broad PR package expansion to an exact-head manual dispatch and validate
    PR contract acknowledgements without restarting compiler CI. Base retargets
    hold a required exact-head/exact-base implementation receipt. Directly changed
    all-ignored integration targets can now opt into required complete ignored-suite
    execution with exact per-test receipts. Empty integration shards skip build
    setup, exact standing receipts prevent duplicate execution, and manual
    expansion batches ordinary targets by package without losing per-test identity.

  • The release workflow extracts the tagged release notes in a preflight job
    before any platform build starts, so a tag whose commit still carries
    changelog.d/ fragments fails within a minute instead of after the builds.
    Previously the check ran only in the publish job, and the v0.18.8 tag failed
    there about 37 minutes in. See
    #2058.

Fixed

  • Preserve literal result claims and producing-operation attribution across pure tensor helper calls.

  • Check host literal result extents in the selected if or match branch and carry callers' claims into shared host callees, preserving primitive attribution and suppressing effects after a failing producer on eval and compiled C.

  • Host signature extent checks cover every declared tensor parameter before the body, even when tensor helpers own only part of the signature. Literal dimensions and repeated binders retain parameter/axis order, and supported higher-order and inline callback invocations preserve their entry checks in C. Private helper schedules avoid repeating checks already discharged at the invocation (#1788).

  • Preserve authored named-extent claims when evaluating gradients of tensor, aggregate and primitive scalar targets, including multiple targets and unused zero cotangents. A rejected forward activation reports its required extent failure instead of producing a gradient.

    Independent calls retain distinct extent claims, including computed result extents. Their guards keep the original producer attribution and preserve the claimed value until the guard executes in generated C.

  • Keep final PR package-expansion reporting on the exact candidate schema while
    retaining trusted stale-head, base, and synthetic-merge validation.

  • Compiled C gradients accept primitive scalar targets alongside tensor and
    aggregate targets, retain forward extent failures even for zero cotangents,
    and reconstruct complete results in written target order, including repeated
    selectors. Primitive scalars and rank-zero tensor results keep their distinct
    public types. Fixes #1934.

  • The wire census validates diagnostics with internal rejection metadata without treating that metadata as serialized numeric data, fixing the missing NonZeroU32 artifact failure tracked in #2048.

  • The interpreter classifies a closure body's execution profile against the
    program's top-level definitions on every application. It now builds that sorted
    definition snapshot once per evaluation and reuses it, instead of re-cloning
    every definition on each call. A chelis test run over a package with many
    definitions and a fold-heavy test is back to its former speed, dropping from
    tens of seconds to about one, matching 0.18.6. See
    #2059.

  • The native C backend no longer rejects a by-value scalar that a tail-position
    user-function call uses in more than one argument slot. Ownership lowering
    moved the single owner on the first slot and then read it dead on the second,
    failing with owner %N is not live (for example g(x) = f3(x, x)). A Copy
    scalar owns no resource, so each slot now duplicates it and the original stays
    live; heap values still move on their last use, so a genuine use-after-move of
    an owned resource stays rejected. See
    #2068.

  • Preserve completed test evidence when optional CI package expansion exhausts its execution budget, while reporting unfinished coverage as unsuccessful.

v0.18.9

Choose a tag to compare

@github-actions github-actions released this 14 Sep 13:10
abff07b

[0.18.9] - 2026-09-14

Added

  • CI publishes a merge-base report of changed Phase 3 required-test definitions and membership alongside the existing definition digests and behavioral oracle.

  • CI publishes changed normative atoms, their registered text, and contract regions beside the existing Phase 4B freeze checks.

Changed

  • Replace protected atom and contract-region checksum updates with required identities, preserved semantic checks and exact PR change acknowledgements.

  • Replace protected-test definition checksums with required change acknowledgements and parsed comparison/result checks, preserving coverage and behavioral controls.

  • Derive parity-corpus membership from Rust test inputs and executable examples, with standing controls for missing or hidden inputs instead of a copied filename list.

  • Phase 4B pull-request validation now requires exact atom and region acknowledgements alongside changed-file acknowledgements, including registry ownership and removed protection boundaries. Existing digests and semantic controls remain active.

  • Document the validation required to retire the temporary Nix workflow digest when its event policy is replaced.

  • Require explicit PR-description acknowledgement of each changed protected test, preserving its definition and behavioral guards.

  • Protected comparison tests now have a standing syntax check for their required calls and executable modes, alongside the existing definition freezes.

  • Document the guard-inventory rule: derive tree membership, retain reviewed dispositions and coverage floors, and never generate semantic authority from a census.

  • Add an AGENTS-linked PR-author guide for test coverage, inventories, protected-contract acknowledgements, rejection messages and the temporary Nix workflow freeze.

Fixed

  • A tensor whose dtype is a declared type parameter can flow through an if/then/else. Previously any such program aborted the compiler with an internal BUG: monomorphization missed precision var message and exit 101 instead of returning a verdict, so a dtype-generic definition containing a conditional could not be checked, built, or run at all. The abort came from a routing predicate that asked whether an if could be lowered to the float DAG and read the node's precision through a reader that panicked on an unresolved binder; an unresolved dtype simply means the predicate cannot establish a float DAG join, so it now answers "no" and the item takes the general host lane. Generic definitions monomorphize at their concrete call sites as before, including under the Float and Int dtype-family bounds of spec/04-type-system.md §5.9, and the §5.8.1 backend assertion is unchanged on the emission path where it belongs (chelis#1541).

  • Restore each integration worker's current test plan after its Cargo cache, so cache replacement cannot remove or overwrite the plan before execution.

  • Phase 3 guard and change-report discovery reject commented-out required tests; PR reports use the validated synthetic merge's actual base, and both Phase 3 and Phase 4B reports select push mode only for push events.

  • Capacity-census liveness now reads final native binding contracts and rejects their retired legacy citations.

  • Typed unsupported lowering rejections now report unsupported_feature through the compiler API, preserving their stage, message and source association; ordinary lowering errors retain lower_error.

  • Host gradients whose original operand directly names a checked function
    declaration, without a captured binding for that target name, now read free
    tensor values from the declaration environment instead of caller-local shadows.
    Required captures reuse successful initialization and preserve entered errors;
    optional shape queries do not initialize absent declarations. Argument order
    and anonymous creation-time captures are unchanged. This does not extend alias
    or captured-target admission, general gradient support, or backend coverage.

  • Selected Host evaluation entries now receive their required caller-supplied tensor
    arguments, including inputs used only by lowered shape witnesses. Missing inputs
    leave the declaration unentered, while dead and unrelated bindings remain
    unobserved. Prepared library calls preserve fresh actuals, existing Random
    behavior, and runtime error ordering. Fixes
    #2013.

v0.18.7

Choose a tag to compare

@github-actions github-actions released this 13 Sep 18:12
4109f8f

[0.18.7] - 2026-09-13

Added

  • Add an internal compiler evidence mode for auditing native Python bindings, recording exact constructor owners, instantiated types, calls and control flow.

  • Native binding compiler evidence now records exact tuple-struct and enum constructor function exposures, including callback arguments, stored values, promoted static tables, reification casts and constant initializers. Its versioned decoder requires the exact occurrence envelope and rejects older incomplete native evidence; collection alone grants no binding authority.

  • chelis build --target hip and --target metal lower first-class count
    ([05-OP-29]) to a dedicated device kernel over the exact Bool8 carrier, and a
    host program whose tensor helper contains count emits that helper as its own
    device translation unit instead of a C body. Both targets previously rejected
    count with a chelis#1291 receipt. The Metal hardware gate records a pass on
    Apple Silicon; the HIP hardware gate remains a documented manual gate, so
    chelis#1291 stays open. See
    #1307.

  • The opt-in chelis-ir/lowering-trace feature records gradient call-site
    specialization, argument and splice maps, caller snapshots, and packed results
    for downstream validation tooling. The feature remains disabled by default and
    does not change ordinary compiler output or language behavior.

  • OpenSpec document changes land by pushing a branch. A push touching
    openspec/** starts openspec-autoland, which classifies the pushed commit
    with default-branch code, opens an internal pull request when every changed
    path is an OpenSpec document, waits for the required checks on that exact
    commit, and merges it. Normative capability specifications are included.
    Anything outside the document set is refused and follows the ordinary review
    path. Inside Devenv, openspec-submit remains an optional local helper that
    validates before you push. See
    #1653.

  • Add IR evaluator APIs for preparing strict-root and selected-plan inputs through
    a fallible provider before execution. Existing evaluator signatures and input
    selection remain unchanged. This is a prerequisite for, not a fix to,
    #1956.

  • chelis prove --tier beacon-only and the Tide prove tool can dispatch self-contained scalar network bounds to Beacon, retaining real-arithmetic qualification, search bounds, unknown reasons and split trees. Search and caller wall budgets are explicit; float execution is not covered.

  • Add tested rustdoc graph and structural carrier-verification infrastructure for
    numeric wire-surface discovery, including private/imported helpers, generic
    closure and fail-closed unsupported codec handling. Live wire formats and census
    activation remain part of the subsequent migration.

  • Added a fail-closed internal adapter for the future #1288 zero-exception capacity receipt. It cannot issue a receipt until the primary, stdlib, wire, and final PyO3 binding gates run on one committed source tree, and retains each nested Cargo/libtest execution's framework outputs beside its supplied fresh receipt. The required stdlib selection includes symbolic generic constructors and imported boolean companions.

  • Release notes can be authored as per-PR Markdown fragments, assembled into a
    versioned changelog section, and published verbatim with the GitHub Release.
    Required PR checks reject missing or invalid fragments and direct changelog edits
    outside release assembly. See
    #1251 and the
    fragment contract.

  • Add the default-off chelis-compiler-api/emission-observer feature for immutable
    observations of the actual ownership-verified payload selected for code generation.
    Ordinary compiler output and behavior are unchanged; observations are not certificates.

  • Add an opt-in ownership-checked C backend entry for closed fixed-control dropout execution plans, with saved-mask replay and unchanged tensor ABI. Ordinary source-build and host-helper admission remain separate work.

  • The default-off native Random observer now carries bounded, ownership-verified
    host source-site and linked call associations, with exact helper-local/full
    source IDs. Unsupported source shapes remain explicitly uncertified; ordinary
    generated headers, public entry ABIs and default emission are unchanged.

  • Add a non-default compiler feature for translation-unit-private observation of
    actual generated-C Random state, including nested saved frames, fixed-control
    forward/replay identities, and copied continuation counters. Public headers,
    ordinary artifacts, runtime behavior, and default features remain unchanged.

  • The opt-in native emission observer now exposes the initial host-lowering snapshot
    alongside the selected verified payload, so consumers can inspect scalar losses
    pruned from tuple-gradient artifacts. Ordinary compilation and wire output are
    unchanged; the additional snapshot is not a certificate or an emitted function.

  • Add the pre-Phase-4C prerequisite runner framework with exact child-command
    identities and execution-receipt validation. It fails when prerequisites,
    per-test outcomes, or required negative and mutation evidence are absent.
    The complete prerequisite set and required CI activation remain outstanding
    under #1296.

  • C fixed-control lowering retains checked Resource requirements as value-free
    source cuts, exposed through opt-in full-spine observations. The existing target
    validator still decides compatibility. Resource-only programs and the evaluator
    keep their prior lanes; existing random-only Rust carriers, wire formats, C ABI,
    and shell interfaces are unchanged.

  • Add an opt-in compiler API that pairs actual selected helper lowering/AD observations with the final successful C artifact. Normal compilation, existing observer layouts and shell/wire interfaces are unchanged; the observations are not correctness certificates.

  • Retain draw-free seed controls and an independent source occurrence census in
    fixed-control evaluation plans, and expose opt-in execution-bearing snapshots
    at actual AD calls. Compiled backend support and public wire formats are unchanged.

Changed

  • BREAKING: Compiler execution JSON advances to version 3 and WireDag JSON to version 9.
    Scalar and tensor carriers preserve exact integers and IEEE floating-point bits,
    including signed zero and NaN payloads. Numeric parameters, extents, references
    and report fields enforce their owning domains on encoding and decoding.
    Readers reject missing, older and future versions; regenerate saved wire
    documents with the current compiler. The wire census now requires executed
    final authority for every discovered numeric leaf and retains no legacy cohort.

  • BREAKING: Python evaluation uses execution wire v3's exact numeric carriers. Floating-point
    values preserve their IEEE bits, including signed zero and NaN payloads; numeric
    scalar results retain their NumPy dtype, with ml_dtypes.bfloat16 for bf16.
    Tensor results retain the declared dtype and exact element values. Old execution
    versions and malformed carriers are rejected. Part of
    #1288.

  • BREAKING: Compiled-context worker handoffs use the same versioned, integrity-checked
    envelope as the disk cache. Workers reject unversioned payloads, incompatible
    formats or compiler builds, and corrupted payloads before reconstructing the
    checked context. Regenerate previously saved worker handoffs with the current
    compiler.

  • BREAKING: The reserved normalize builtin is removed. An undeclared call reports an
    unbound variable; define an ordinary function with an explicit normalization
    formula, as in examples/explicit_normalization.ch.

    Builtin and canonical RISC semantic identities now have an exhaustive normative
    atom registry and an executable closure oracle. See
    #1299.

  • BREAKING: Deep AST annotations use dedicated types for compiler-owned metadata and a
    separate extension map. Rust API consumers must migrate from MetaMap.entries
    to Metadata and its typed accessors. Parsing, deserialization, and typed
    construction reject malformed annotation values and duplicate keys, including
    custom and span_* keys; preserved macro-source arguments remain raw syntax
    data. See #1604.

  • BREAKING: chelis check <dir> emits one typed envelope, {"files": [...], "errors": [...]}, with both members always present, and it never exits 1. Three behaviours change for a directory target. An empty directory now fails with an empty_corpus error and exit 2; it used to pass. That includes a directory whose only sources sit under target/ or a dot-directory, and the message counts those. A directory the walk cannot read no longer discards the whole run. Every readable file keeps its report, the unreadable directory is one directory_walk_error, and the command exits 2; it used to exit 1 with nothing on stdout. A populated directory's document now carries errors, which it used to omit.

    The walk shared by chelis check <dir> and chelis test <dir> also changes. Each file and directory is visited once, by canonical path, so a file reached through several links is checked or run once. A dot-named or target link is skipped by its name before it is resolved, so it no longer aborts the walk when its target cannot be read. A link that cannot be resolved is still an entry when it has a source name. For check, any other unresolvable link is a `...

Read more

v0.18.6

Choose a tag to compare

@github-actions github-actions released this 29 Aug 04:28
cf49f85

What's Changed

  • Name the newline, not the semicolon, in the v0.19 block separator diagnostic by @rlronan in #1272
  • (#730 class): report an abandoned test batch and keep a sibling def out of an explicit import by @rlronan in #1273
  • Resolve host-lowering constructor references on their full mangled name (#1271) by @rlronan in #1275
  • (#788 class): fix the registry dep graph, preflight the conform write path, widen §4 citations, settle skill uniformity by @rlronan in #1279
  • fix(scripts): regenerate the compile-fail fixture locks on a version bump (chelis#1128, chelis#1234) by @glampouras in #1233
  • B2 (#908 unrepresentable AST): one stamped Deep ingress for the compiler API (chelis#1088) by @rlronan in #1285
  • Make DeepTag realizability classification exhaustive by @rlronan in #1301
  • docs(types): correct #1207 evidence and level-generalization plan by @vrk210 in #1227
  • Specify grounded dtype capabilities and Phase 4 delivery by @rlronan in #1283
  • (#729 Phase 4B follow-up): relu adjoint, stop_gradient barrier, JsonBigInt ingestion, gap receipts, exact matmul/einsum by @rlronan in #1317
  • Consume target-aware root manifests across eval, C, and HIP by @rlronan in #1310
  • Replace environment-wide type generalization sweeps with solver levels by @rlronan in #1318
  • B4 (#908 unrepresentable AST): stamping fidelity — [03-ROLE-1..3], variant dispositions, hygiene recursion (chelis#885, chelis#1087) by @rlronan in #1319
  • Make function inference SCC planning linear by @rlronan in #1328
  • Hand the gate's built chelis to the #908 oracle and lock the build-before-oracle ordering by @rlronan in #1325
  • Correct trace rank-zero test contracts (chelis#1148) by @rlronan in #1326
  • Implement first-class bool tensor count and WireDag v6 by @rlronan in #1303
  • Materialize proven zero gradient roots by @rlronan in #1331
  • Make nested front-end lowering linear by @rlronan in #1332
  • docs: codify agent workflow and repository operating rules by @rlronan in #1335
  • docs: generalize agent ownership boundaries by @rlronan in #1337
  • Implement final capacity-authority foundation by @rlronan in #1336
  • fix: diagonal/trace retained-axis index mapping in both lanes (#1349 memory safety) by @rlronan in #1353
  • #1286 (compiled value ownership): free each root allocation exactly once in the C host emitter (#1222) by @rlronan in #1340
  • Replace the public C numeric ABI with exact tagged carriers by @rlronan in #1308
  • Align the exported stdlib numeric surface with [05-OP-35] by @rlronan in #1302
  • docs: define hash-order determinism plan by @rlronan in #1366
  • Phase 1 (#730 loud unsupported): stop dispatching four-byte HIP kernels over one-byte bool buffers by @rlronan in #1365
  • Implement direct checked subtraction and exact extrema selection by @rlronan in #1309
  • Make tensor closeness use each float dtype's arithmetic width by @rlronan in #1304
  • spec+docs: decide runtime extent rules and trim the #1277 plan by @rlronan in #1343
  • docs: scope red-team findings to pull request by @rlronan in #1381
  • docs: amend hash-order plan to a type-level mechanism and decide settlement order by @rlronan in #1370
  • release: prepare Chelis 0.18.6 by @rlronan in #1385

Full Changelog: v0.18.5...v0.18.6

v0.18.5

Choose a tag to compare

@github-actions github-actions released this 23 Aug 00:14
6602f01

[0.18.5] — 2026-08-22

Three breaking entries, all in the front end: the checker rejects
polymorphic recursion, the parser rejects an integer literal in a bare
type position, and > evaluates its operands in the order they were
written. Each one either rejects a program that used to be accepted or
runs an accepted program differently; each entry names what to change.

Every on-disk compiler cache is invalidated, twice over. The
compiled-context, stdlib, and library cache formats advance (8 → 9,
4 → 5, 1 → 2), and cache keys now carry a build fingerprint rather than
the release version. Both are automatic: the first build after
upgrading is cold, and nothing needs to be deleted by hand.

Added

  • Recursive generic host calls compile via bounded memoized
    monomorphization (chelis#1158, successor to chelis#941).
    The C-emitting
    host lane now compiles a recursive (direct or mutual) generic host call by
    emitting one specialized definition per distinct checked type application,
    memoized so each (function, instantiation) pair is generated exactly
    once, with recursive edges lowered as ordinary calls to the owning
    specialized symbol (<def>__mono_<hash>, deterministic across builds).
    The former branded requires bounded monomorphized symbols rejection is
    retired for these programs; a surviving generic call whose checked type
    application never resolves still fails closed with a branded unsupported:
    diagnostic citing chelis#1158, and no C artifact is written. HIP and Metal
    targets consume the same shared host lowering. Acceptance oracle:
    cargo nextest run -p chelis-cli --test recursive_generic_monomorphization --no-fail-fast.

Changed

  • BREAKING (checker): polymorphic recursion is now a check-time type
    error ([04-INF-2]/[04-INF-3], spec/04-type-system.md §3.1.1).
    Every
    recursive call inside a recursive binding group must be typed at the
    caller's own instantiation; a call whose type application embeds a type
    variable inside a larger constructed type is rejected by the checker with
    a diagnostic naming the function, both instantiations, and the deciding
    atom — identically across the eval and build lanes. Previously the eval
    lane accepted and executed such programs (they never compiled natively);
    the reproducer below now rejects at check:

    type Box[a] =
      | Empty
      | Full { value: a }
    def f[a](x: a, n: int32) -> int32 =
      if n <= 0 then 0 else f(Full { value: x }, n - 1) + 1
    def main() -> int32 = f(1, 3)
    

    Fully concrete (type-variable-free) recursive type arguments remain
    admitted for signature variables introduced by inference rather than
    authored [a] binders, so partially annotated recursive defs keep
    checking exactly as before.

  • BREAKING (parser): an integer literal in a bare type position is now
    a parse error (chelis#1179).
    The Rust Surf parser shared one integer
    arm between ordinary type atoms and dimension items, so an integer
    parsed as a type anywhere a type was expected and desugared to a fresh
    type variable — def g(a: 732) -> f32 = a produced (t-var {} 732)
    and chelis check scored it 1.0. The Tree-sitter grammar already
    rejected the spelling, so the two parsers disagreed. Both now agree: a
    bare type position rejects with "expected a type; integer literals are
    only dimensions inside tensor[...]" naming the byte offset, in the
    canonical and legacy v0.18 parse modes alike. The two dimension
    positions keep accepting integers: tensor shape items
    (tensor[3, 4, f32]) and type-application arguments, whose intended
    meaning is the concrete dimension instantiation of a
    dimension-parameterized ADT (Frame[2], chelis#940). The spec grammar
    gains the previously missing TypeArg <- TypeExpr / IntLit
    production, and the Tree-sitter applied_type rule matches it, so
    Rust/Tree-sitter parity holds on both sides of the split. This is the
    parser half only: the checker does not yet enforce an integer
    argument's kind or extent (Option[732] still checks, and a
    Column[3] return holding a 2-element column still scores 1.0);
    chelis#1247 tracks that surviving mechanism.

  • BREAKING (eval/build): > evaluates its operands in authored order
    (chelis#1180).
    Surf desugared a > b as the operand-swapped
    cmplt(b, a), and Deep application evaluates its arguments left to
    right, so the right operand's effects and traps ran before the left
    operand's in every executable lane:

    verdict = lhs() > rhs()   -- printed RHS then LHS, in eval and compiled C
    

    > now desugars to the existing gt built-in with the authored
    operand order. Comparison results are unchanged, including false on
    either NaN operand, because gt's value is defined as cmplt(b, a)
    over the already-evaluated operand values
    (spec/05-risc-primitives.md §3.2). Effectful operands now run left
    to right in both lanes with byte-identical stdout, and when both
    operands trap, the left operand's trap surfaces; a program that
    observed the reversed order changes behavior. One further consequence
    of the retarget: gt borrows both arguments where cmplt consumed
    its second, so >'s operands stay usable afterward
    (mask = a > b then where(mask, a, b) now checks) while the
    equivalent b < a spelling still consumes — a strictly monotone
    loosening; the family-wide inconsistency is tracked at chelis#1248.
    The general operand evaluation-order rule is now normative at
    spec/03-deep-syntax.md §4.4, and the Deep consumers that matched the
    closed comparison-name set (the D-WF invariant grammar, its
    boolean-shape check, constant folding, and the Tier B / opaque SMT
    lowerings) admit the gt spelling, so @invariant(p) p.value > 0.0
    keeps working end to end.

  • Compiled-context caches are keyed on the build, not the release
    string (chelis#1156).
    CacheIdentity and stdlib_cache_key folded in
    COMPILER_VERSION, which names a release: every binary built from any
    commit carrying the same workspace.package.version reports it, so two
    binaries that disagree about type semantics shared cache entries and
    checked programs under each other's rules. Measured on a released
    v0.18.2 binary and a post-chelis#1130 main binary — both reporting
    0.18.2 — sharing one cache dir: the release binary accepted and
    evaluated a program it correctly rejects cold, and the main binary
    raised a spurious precision mismatch: expected int32, got int64 on a
    program it correctly accepts cold. Both keys now extend the release
    string with a build fingerprint taken from the running object image
    (Mach-O LC_UUID / ELF NT_GNU_BUILD_ID, or a whole-image SHA-256 when
    the image carries neither), derived by the new leaf crate
    chelis-image-id. A same-content copy at a different path still shares
    the cache; a different build takes a clean miss rather than a stale hit;
    a version bump alone still flips the key. If the running image cannot be
    identified at all, the fingerprint falls back to a per-process
    discriminator and warns once, so a degraded process shares with nothing.
    This mostly bites people who build from source — worktree binaries, CI
    matrices, and images that vendor a pinned commit and label it with the
    workspace version.

  • Runtime diagnostics name a value by its dtype and canonical digits
    (chelis#997).
    The JSON, CSV, and eval runtime diagnostics rendered
    numeric payloads through derived Debug, which spec/05-risc-primitives.md
    [05-OBS-1] already forbids: a diagnostic is one of the exits that must
    "emit text that parses back to exactly the stored bits at the value's own
    dtype width". Reported text changes shape — got f32 0.1 and
    got int64 9007199254740993 in place of the Rust representation, a
    malformed ADT field list as malformed JNum fields [int32 5], an absent
    argument as got nothing — and below the top-level dtype tag the
    diagnostic channel now agrees byte for byte with the exit channel. The
    half widths were misreported outright before this:
    half::f16's Debug
    forwards to to_f32(), so a stored f16 that every exit renders as 0.1
    appeared in a diagnostic as 0.099975586. Anything that scrapes runtime
    diagnostic text needs updating; nothing about the values themselves
    changed.

  • Compiled-lane CSV ingestion is linear (part of chelis#943). Reading a
    9588-row by 8-column, 420 KB CSV in the compiled lane allocated 3055.6 MB
    and held 2189.9 MB live; it now allocates 121.0 MB and holds 91.4 MB, a
    25x reduction in allocated bytes. Std.Io.Csv.parse_rows moves off
    recursion onto map/fold (one line carried two independent O(n²) costs:
    append deep-cloned the accumulated rows per row, and drop(lines, 1)
    cloned the remaining tail per row), chelis_string_slice locates byte
    offsets with char_indices() and takes an O(1) all-ASCII fast path
    instead of materializing a Vec<char> per call, chelis_string_len
    returns a cached count instead of recounting, and chelis_list_append
    reserves len()+1 so the following push does not realloc. Character
    indexing and multi-byte behavior are unchanged, and try_read_csv still
    returns None on a ragged row or an unterminated quoted field. Honest
    cost:
    each RuntimeString grows by exactly 8 bytes for the cached
    count, so a workload that builds many strings and never slices them or
    takes their length is a net allocation regression. Not fixed here: JSON's
    parse_array_rest/parse_object_rest are still quadratic, and a
    user-written recursive append accumulator is still quadratic —
    chelis#943 remains open for both.

  • The Rust toolchain is pinned to 1.98.0 (PR #1236; chelis#1237 for the
    Nix half).
    rust-toolchain.toml tracked the stable channel, so a Rust
    release reached every branch at once with no staging; 1.98.0 (released
    2026-08-20) tightened two lints and turned the wh...

Read more