# Changes since alpha.1

URL: /docs/changes
Markdown: /docs/changes.md

> Current-main behavior, migration notes and limits after the published preview.

These docs track **main**, including [Unreleased changes](https://github.com/icantcodefyi/sakuralang/blob/main/CHANGELOG.md#unreleased).
The published npm `latest` is `0.1.0-alpha.1`; use a [source build](/docs/quickstart#install-from-source)
for the changes below until a newer release publishes. This page does not declare
M4 complete or stabilize experimental native execution.

## Sources, diagnostics and portability [#sources-diagnostics-and-portability]

- Sources now end in `.sak`, with no old-extension fallback. Rename old files with
  `git mv main.sakura main.sak`. `sakura.toml`, the `sakura` language id and
  `sakura:///std/…` URIs stay unchanged.
- CRLF and LF have the same meaning, including newlines in strings. `sakura fmt`
  writes LF; `fmt --check` identifies CRLF as a formatting difference. A snapshot
  differing only in line endings is reported as a line-ending mismatch. Scaffolding
  writes `.gitattributes` with `*.sak text eol=lf` and `*.snap -text`.
- JSON file URIs follow RFC 8089, including `file:///C:/dir/main.sak`. Terminal
  diagnostics use OS paths, without URI escapes. MCP path filters accept equivalent
  file URI spellings. On Windows, Node lookup honors `PATHEXT` and solver lookup uses
  `.exe`. These fixes are not a claim of a supported Windows release.
- `V1002` on `Type.member` can suggest a unique prefix/extension match when there
  is no near spelling: `List.lenght` → `len`, `List.fil` → `filter`. These fixes are
  `suggested`, never automatically safe; `List.size` no longer suggests `slice`.
- `V2002` names a declared callee and its module, with expected and actual argument
  counts. A function-value call retains the type form. `V3001` can propose the
  effect's unique deterministic std handler; that edit remains `incomplete`.
- Syntax-error suppression follows **place**: diagnostics whose spans point into
  the broken body are suppressed, as is its cascading `V3001`. An error in another
  module used from that body remains visible. Rechecks invalidate refinement
  dependencies through contracts and no longer duplicate reused tests' diagnostics.
- `V4004` limits a `match`, `catch` or `let` pattern decision tree to 1,048,576 nodes, not a fixed arm count.
  Or-pattern combinations and copied wildcard rows can expand the tree. It is
  diagnosed during lowering (`build`, `run`, `test`), not `check`. Thousands of
  flat literal or guarded rows can compile and run; split combinatorial matches
  into one column at a time. See [`V4004`](/docs/errors/V4004).

## Analysis cache and check latency [#analysis-cache-and-check-latency]

`check`, `build`, `run`, `test`, LSP and MCP reuse std analysis from a disk cache,
one file per compiler build at `<cache>/sakuralang/std`. The base is
`SAKURA_CACHE_DIR`, otherwise `XDG_CACHE_HOME` or `~/.cache` on Unix and
`LOCALAPPDATA` on Windows. `SAKURA_NO_CACHE=1` disables it. Missing, damaged or
foreign entries are recomputed; files are disposable and have no format guarantee.
This is separate from the authenticated [proof cache](/docs/agents/verify#proof-cache-and-build-behavior).
Concurrent test jobs should set distinct `SAKURA_CACHE_DIR` directories.

The recorded release-build measurements on one quiet macOS host fell from about
22 to 7 ms for `sakura new cli` and from 54 to 39 ms for a 10k-line check. These are
observations, not platform guarantees. Long-lived servers also reuse std in memory.
`bench/bench compile --json compile.json` reproduces the compile-latency workloads;
its cache lives in `bench/target/cache` unless overridden.

## Server limits and failures [#server-limits-and-failures]

`Serve.host()` bounds request bodies to 1 MiB. It rejects oversized bodies with
`413`, closes the connection and never calls the handler. The following additional
cases are distinct:

| Condition | HTTP response | Diagnostic and lifecycle |
| --- | --- | --- |
| Reply status or headers cannot be sent | `500` | `D091`; server keeps running |
| WASM request exceeds `SAKURA_MAX_COPY_BYTES` | `413`, connection closed | `D091` on stderr; handler not run, server keeps running |
| WASM handler response exceeds that budget | `500` | `D091` on stderr; server keeps running |
| WASM heap cannot admit a request | `503`, `retry-after: 1` | `D096` on stderr; server keeps running |
| WASM runtime trap during a request | `500` | One `D090` naming function and PC; server stops and program exits 70 |
| Request still waiting when program ends | `500` | One `D090` “server stopped”, unless a defect already ended the program |
| Request arriving on an open connection after server stops, either backend | `503`, connection closed | Handler never runs |

WASM tests resume after host faults or traps on a fresh instance. Invalid host
operation indices or result shapes are `D090`; exhausted request tables are `D091`
for `Clock.sleep` and `serve_listen`. Defect records now carry JS-compatible messages
and source spans from `sakura.debug` (omitted by `--strip`).

## HTTP, cassettes and filesystem security [#http-cassettes-and-filesystem-security]

`SAKURA_MAX_COPY_BYTES` must be decimal digits representing 1 through 2^53−1.
Empty, zero, signed, fractional, exponential, hexadecimal or spaced values cause
`D091` at first use; unset it for the 256 MiB default. JS `Http.host().send` now
bounds the response body too. An oversized HTTP response is `HttpError` with
“response too large”, not a runtime defect; use streaming for larger responses.
A connection lost while reading a body is also `HttpError` on JS and WASM.

`test --record` stores at most 16 MiB of a sent or streamed response; exceeding it
fails with `HttpError` without appending the exchange. Malformed exchanges fail
when the cassette opens. Recording requires JS; WASM can replay the cassette and
run `test --contracts`. Tests without `--record` have empty host permissions;
`Test.fixtures()` retains its own grants. Secret HTTP headers work on WASM and
remain redacted in cassettes. `Secret.map` now requires an explicit trust reason,
and `sakura audit` lists it beside `Secret.expose`.

Writes through dangling symlinks inside an `fs.write` grant are refused with
“escapes the directory”, even when the link points back inside the grant. This
covers `Fs.write`, write/append `File`, cassette recording and `Sql.sqlite` on JS
and WASM. **Write to the target path instead of the dangling link.**

## Verification and performance [#verification-and-performance]

[`sakura verify`](/docs/agents/verify) reuses authenticated verdicts, including
`unknown(resource_limit)` at the same budget, and replays eligible i64
counterexamples. JS and WASM builds remove eligible proved checks; total predicates
go with them, while predicates calling functions still execute. `requires`, entry
checks and checked arithmetic stay in this proof-removal pass. Proof key ownership,
directory permissions and location are checked; builds never create the key.

Direct WASM local-handler/callback calls, fused collection access, tag comparisons,
tail-call loops and large-match lowering reduce work while preserving semantics.
Core output and snapshot hashes are now independent of earlier analyses in a
process; std-first file ordering can change dump IDs and hashes. Finished WASM
root tasks are freed between benchmark samples.

For dense integer IDs, prefer `List` to `Map`, as the DeltaBlue benchmark now does.
Both return updated values: assign the result back. Use `Map` for sparse keys.
The [benchmark guide](https://github.com/icantcodefyi/sakuralang/blob/main/bench/README.md)
documents `bench/bench envelope wasm.json native.json --json envelope.json`.
Inputs must share commit and host; the native file must use the **matched** lane.
Exit 0 means a report was produced, not that every ceiling passed. Loaded-host and
CI results are advisory. The native comparison ports are Rust/Go/OCaml/Koka,
not a stable Sakura native backend.

## Distribution and editor tooling [#distribution-and-editor-tooling]

See [Install](/docs/install) for the published platforms and next-release additions:
Linux ARM64 (glibc) archives and npm package, coming in v0.1.0, and Windows x64 MSVC
ZIP, npm and `install.ps1` packaging, coming in v0.2.0. Neither is in alpha.1.
New macOS tag releases require Developer ID signing (team `P62B65A837`) and Apple
notarization before archives and packages are assembled; this does not retroactively
sign alpha.1. Bare Mach-O executables cannot carry a stapled ticket. The installer
checks checksums and, when `gh` is available, requires successful attestation
verification; without it, it reports “integrity verified, not attested”. Custom
prefixes must be absolute.

The [tree-sitter grammar](/docs/editors#tree-sitter) is available in the repository.
The generated skill, manifest and ZIP normalize Windows separators and are
byte-identical across OSes. The skill entry point retains a 7,000-byte source
working target and a tested 7,500-byte limit after release metadata is filled.
