# Changelog All notable changes to revl are recorded here. Versions follow the IR contract: a major version is an IR-breaking cut, a minor version adds without breaking. ## 0.3.0rc1 (unreleased) - the 0.3.0 release candidate 0.3.0rc1 is the release candidate for revl 0.3.0. It is a beta: unreleased, and open to change before 0.3.0. It is the minimal clean, self-consistent six-tier cut plus the live-systems demo: self-host front end, cross-tier execution, six-backend conformance, and per-tool-call agent effects on all six tiers. It also folds in the full agent story and the standing correctness debt. ### The language - Self-hosted front end. `selfhost/lexer.rvl` and `selfhost/parser.rvl` compile at ir_version 3 and are cross-checked against the reference implementations as a differential oracle: two independent implementations of one grammar forced to agree, so a disagreement is a real defect in one of them. - Six-backend conformance (py, ts, rust, go, java, wasm). The autogenerated conformance matrix is published and green; the compiler emits itself with no divergence on py, ts, and java. - Typing frontier narrowed: match-arm checking in check-position, generics instantiation, `type X = Y` alias resolution matching TypeScript, and six accept-but-won't-compile holes each verified against `javac` and `cargo` and closed. This is not a claim that none remain; the open ones are listed under Known issues. - **Declared resource dimensions (issue #1938, fixed by #2149).** A composition can now declare its own capability's resource parameters: `capability mail.send(account: discrete, folder: path)`. The parameter is bound from the call arguments and then appears in the approval ticket's spelling and in the audit token, so `revl audit` records which mailbox is sending instead of the fact living only in a gate's own spelling. The kind vocabulary stays closed (`path`/`discrete`/`ceiling`, the three orders the core vocabulary already used) and the registry stays closed against UNDECLARED names, so a typo is still refused at parse. A composition that declares no dimension is byte-identical everywhere; `INTERCHANGE_VERSION` stays `1.0` (the declaration is an IR member, not an interchange member). See docs/design/294-parameterized-capabilities.md. - `try e`: Result propagation in a fn body (#1967). Expression-bodied top-level fns, template scope in block-bodied match arms, and `Float.to_str` let eight realistic components compile on the first draft (#633, #570, #703). - The `Stream[T]` reactive surface across the tiers: `every ... in` iteration, `on ... as` typed-event handlers, the map/filter/take combinator chain, backpressure policies and the block drain window, provider-declared replay with its last-n backlog, and the required `Stream[T]` coeffect. A tier that cannot lower a form refuses it by name (#507, #636, #679, #747, #792, #857, #982, #1042, #1060, #1066, #1076, #1105, #1179, #1523). - Endpoints: a `route` clause with derived artifacts, an opaque `Principal` with an admission refusal for forged authority, and the `host` row bound to the Cordis server (#775, #782, #787). The stdlib gains typed HTTP contracts (`Request`/`Response`, `Handled`/`NotHandled`), the canonical request-framing primitive, HS256 JWT verification, and context-scoped templates with per-hole escaping (#745, #881, #1556, #861, #793). - Frontend assets: typed, jailed, content-pinned asset handles, the insertion-site source map chained into the bundler's, run-time reads of pinned assets, and a reactive data channel declared in revl and projected to the frontend (#1043, #1057, #1064, #1092, #976). `revl dev` runs the scoped WebUI host and the Vite frontend as one command (#833, #984). - Persistence: a driver-independent typed-model store with store-owned key assignment, a memory driver and a sqlite driver (#767, #778, #806). `revl gen-types` turns a typed model document into revl types and checks its digest at compile (#1958). - A predictable service lifecycle contract (startup, readiness, cancellation, disposal, hot replacement), with native conformance coverage (#760). - `extern local`: a durable write recorded without being a crossing (#2177). - Declared intent bounds an operation, not its emit steps. The intent-versus-action refinement kernel, the explicitly-related object and set-valued scope, the stating slot for a value-returning crossing, and the comparison of a standing grant as a declaration at the class-(c) gate (#855, #971, #1007, #1031, #1058, #1061, #1073). - Retention: the `Retained[T, ]` qualifier, a signed erasure receipt (`revl retention-receipt`), and a refusal of a past-deadline value that a provider body persists (#988, #1018, #1177, #1002). - Cross-tier divergences closed in `Int.MIN` arithmetic, Float division and remainder, Str ordering and character access, `${...}` rendering, list bounds, closures, and numeric parsing, plus short-circuit `&&`/`||` on wasm (#1086, #1118, #1082, #1106, #1094, #1088, #1138, #1039). Wasm refuses `Int / Int` by name instead of lowering it to a signed divide (#2235). - Refusals added: an undeclared nominal type is `T-UNRESOLVED` rather than a mismatch (#863, #902); a statically out-of-range list index (#951); a non-finite Float in `json_stringify` (#945); an ADT case that collides with another module's name (#1146); an `extern fn` and a `fn` of one name (#1880); two component names that differ only in case or underscores (#2272); a method call on a record in a component body (#1687); a capability token that cannot match, which is now a parse error so a deny rule cannot go silently inert (#1992); a null-ambiguous `Opt` at the JSON boundary (#1270). `pub` names are module-scoped (#2005) and local bindings shadow outer declarations in the resolver (#1990). - `revl grammar` exports a source grammar derived from the parser, `revl_grammar {prompt: true}` serves the complete prompt grammar, and `revl idiom` serves the minimal correct example of each construct (#1663, #2175, #1761, #2283, #2298). `revl test` gains `--list`, `--filter` and `-v`/`--report` (#866, #882). `revl explain` covers every diagnostic code with a `fix` and a `guarantee` (#2048). JSON diagnostics name the declaring file relative to the invocation and `--json-diagnostics` is registered on all six verbs that read it (#2072, #2226, #2030). - The tree-sitter grammar covers syntax 2.0 and its conformance gate is green (#639). - Self-host: the self-hosted emitters caught up with the reference and the last three tier emitters are wired into the native compile driver (#1032). `emit_go.rvl` has zero go divergences, `emit_py.rvl` zero py refusals and divergences, `emit_rust.rvl` is driven natively and held to the reference's bytes, and the front end gained the type layer (type-spelling algebra, expression types, provide-method and component-body types), and the self-host admission gate enforces the fn-body binding discipline (#1624, #1645, #1143, #534, #1154, #1160, #981). The native IR producer carries tests, fault tests and an extern's host refs (#2182, #2162, #2145). - The formal stack landed together (G4 deferred and approval, G5, G6, G1, A1, declaration rules), with differential-oracle rows for G9, G-RETAIN, G-COUNCIL-SPLIT and the witnessed-extern `undo` rule (#1853, #2081, #2082, #2053, #2122). - Java: an annotated empty list is declared as its element type, not as a raw `java.util.List`, so a loop over one (as in `dedent` in `stdlib/str.rvl`) compiles under javac (issue #2296, fixed by #2302). - The self-host native driver links a used module's externs. It now builds the same extern closure as `compile_files`, so a module that uses `stdlib/json.rvl` keeps its json extern bodies on py and ts (issue #2181, fixed by #2305). - Go: an `Int32` `+`, `-` or `*` in a component method body traps on overflow, and so does unary minus, as a top-level `fn` already did (issue #2262, fixed by #2312). - Service names that differ only in case, and hot-swap names that fold onto a running component, are refused on every tier (#2309, closes #2275, #2276). - A call in a fn body whose callee is a local binding takes its arity and types from that binding, not from a same-named private fn in another file (#2319, closes #2311). - Self-host refusals match the reference. Statement-level parse errors are refused (#2395, closes #2359), as are the if, match arm, fn and tuple type forms (#2398, closes #2396) and block arms the reference cannot lift. Test and prop-test bodies are checked too (#2400, closes #2399). - Self-host name resolution: a `use` resolves as the reference does, and an unexported name is refused (#2369, closes #2363). An imported service's record types are carried into the native link (#2327, closes #2308), and applied generic records compare by type argument (#2404, closes #2403). - Self-host reference refusals are named as the reference names them: UNSUPPORTED on java and wasm (#2388, closes #2383), and on rust and ts (#2377, closes #2316, #2353, #2367). Reserved ts service keys and namespaced keys are named on py, rust and java (#2392, closes #2389, #2390), and ts mangles namespaced host keys as the reference does (#2387, closes #2384). - Arithmetic in a component method body now executes on py, go, rust and java (#2376, closes #2198). - A rowless running component is named on the manifest wire (#2375, closes #2289, #2334, #2351). - `str_char` covers every code point through a new `Int.to_char` builtin, so `str_char(13)` no longer returns an empty string (#2295, closes #2285). - The ts tier keeps string index scans linear when the loop reads other strings (#2320, closes #2313). - The grammar summary names `capability` and `grant` (#2370, closes #2280), and `Secret[T]` has a secret-value idiom served by `revl idiom` (#2331). - The ts tier renders a structural record type in a signature, so a block match arm that captures a record no longer emits an unclosed parameter type, and a list of a union is parenthesised (`(bigint | undefined)[]`). The self-host ts emitter matches byte for byte (#2300, closes #2297). - The java tier boxes `Int32` and spells `Any` in v3 type arguments, so `List[Int32]` and `List[Any]` compile, and a generic record declares and applies its type parameters (#2426, closes #2303). Int and Int32 overflow on java now fail with `revl: Int overflow` and `revl: Int32 overflow`, as docs/arithmetic.md states for the other hosted tiers (#2425, closes #2381). - The go tier passes a component `fail` message to `fmt.Errorf` as an argument, so a `%` in the message is returned verbatim instead of read as a format verb (#2427, closes #2379). - A function can state a spec with an `ensures` clause over its parameters and the returned `result`; it expands to a `prop test`, so the type-derived generator, the shrinker and the minimal counterexample all apply, and `revl audit --spec-results` lists each spec with the result of its last recorded check (#2705, docs/function-specs.md). ### The agent story - Per-tool-call agent effects (H1) end to end on all six tiers, with residue enumeration. - Witnessed-inverse externs: an extern whose checked inverse is another host extern, so the reversible/irreversible boundary moves from fixed-by-the-world to moved-by-engineering. The six-tier teardown replays inverses on unload and abort, and `compensate` adds typed best-effort compensation on the audit surface, distinct from the proof surface. - Session commit protocol: deferred emissions enqueue and flush at a two-step commit, and abort drops the queue and replays the witnessed inverses to prove a clean world. - The approval gate (auto-approve unless irreversible): a call's action class is the worst class over its whole reach. Class (a) revertible auto-approves silently, class (b) deferrable enumerates at commit, class (c) immediate prompts per call with an exact-hash single-use ticket. Standing grants let an operator mint a TTL or uses-scoped approval once; revocation retires it. - Crash recovery: the WAL is the gate's authority. Recovery replays inverses, re-prompts a class (c) crossing rather than re-firing it, and refuses to resume a policy-recorded snapshot into a policy-less session. - **An agent-authored source that wires a service whose `emission` names no token is now refused: by `revl_load` and `revl_swap`, not only by `revl_admit`.** A method that spells `emission` bare declares an effect and declines to say what it reaches, so nothing in the composition states that it stops short of the admission kernel, and the kernel-boundary fold can no longer read it as provably disjoint from the kernel. This is the arm issue #1265 sequenced behind the composition half (PR #1292) and issue #2105 took: the refusal asks the declarations, not the token namespace, so a candidate that composes only pure services is unaffected. The repair on the operator's side is one word: write `emission[]`, naming the boundary the body crosses, and every shipped service already does: `stdlib/server.rvl`'s per-row `Server` included, whose synthesized externs now carry the shim's own boundary token rather than the row's label. First-party compiles are unchanged; only the untrusted-author profile the agent verbs ride is tightened. - Model placement as a checked route condition: `model role`, `route model`, an origin ceiling on the role, the role's device profile and candidate set, and `prefer resident` as a per-arm opt-in to residency-aware ranking (#1220, #1243, #1423, #2144, #2159). A model council with role-bound members and typed disagreement gives its refusal its own guarantee code, and its answer type keeps a disagreement from being ignored (#1257, #1302, #1389, #1392). - Model decisions leave evidence: a signed `ModelDecision` record with a fail-closed verifier, sealed at the crossing and replayable from the artifact (#1239, #1286). A validated emission's response type compiles into a decoding grammar, and providers apply constrained decoding through runtime adapters for the common hosted-model wire formats (#1262, #1326, #1524, #1531). Five tiers refuse a `validated` emission instead of dropping it (#1391, #1413). - Models are scheduled onto declared devices: the conductor picks one candidate per routed model action before anything spawns, hands each host's schedule to its child and enforces it there, provisions each role once, and asks the servers what a host holds at plan time. When no candidate fits, the placement is refused (#1468, #1478, #1568, #2106, #2180). - Promotion from recorded evidence: shadow agreement accumulates over the decision records and gates a promotion, a landed promotion changes which model answers, and a promotion is decided from signed execution receipts rather than a test (#1250, #1297, #1309, #1439, #2104, #2174, #1305, #1363). - Self-evolution pieces: an evolution controller with one lifecycle and one verdict, a reward folded from repository counters, held-out scoring on a draw the candidate could not read, a curriculum derived from repository artifacts, and a declared evolvable-layer state with a rollback that reports what it compensated (#1241, #1231, #1258, #1424, #1448, #1230, #1232, #1256). The eval honesty protocol (EVAL-1) is frozen and checked (#493, #583). - Computer use: a reversibility classification, UI transaction phases with a five-way revert split and an operator confirmation gate, taint classes derived instead of asked, and a transaction unit that settles what it registered when a call fails on py, ts, go, rust and java (#1242, #1287, #1284, #1320, #1412, #1969, #1971, #1973, #1977, #1978). - Quorum approval: multi-party approval with a durable decision graph, operator verbs for escalate, revoke and override, an admission receipt, and a vote bound to a credential with a lifetime instead of a name (#865, #974, #994, #1019, #1035). A policy clause can mark a capability never-standing, enforced fail-closed (#2010). A distilled auto-approve rule keeps its budget and review bind across generations and forks (#983, #995, #998, #999). Delegation hands a scoped, revocable resource between components, bounded at depth one (#456, #589). - MCP: `revl mcp proxy` gates an existing MCP server with no `.rvl` written, `revl serve --http` and the MCP HTTP transport answer per-request operators with SSE streams, and tools are disclosed in tiers with a `revl_verbs` index (#1535, #554, #1557, #1505, #1746). Every mutating response carries its exact undo, `step_back` reverts the last change, and speculation is the default with one explicit commit (#1721, #1741). `revl_state` reports the six agent-loop axes, `revl_counterfactual` replays a `revl_act` log under a what-if, and `revl_swap` and `revl_change` carry a blast radius (#1759, #1763, #1874). Every verb is callable by a function-calling model, and `revl_source` enumerates the packaged stdlib (#2076, #2179). A swap or load that does not succeed leaves the running composition serving (#1445, #1447). - `fillSpec` v2 and scaffold: a crossing is a permission with its call-site form, each hole names the grammar category of its fill, a hole whose crossings span two capabilities is split, and a pure Unit method's fill is refused (#1655, #1657, #1671, #1662, #1865). - Replay and simulation: `revl replay` with replay-mode readiness, counterfactual incident replay under an alternate policy, and `revl simulate` policy-diff over a recorded WAL (#635, #849, #852). `revl goal` audits termination contracts and reports blind spots (#452, #520, #593). `revl analyze` searches a Petri net of the composition for a reachable dead state (#399). - A compositional blast-radius benchmark scores an agent on the six axes `revl_state` reports (#1737, #1764). - A cold `revl_change` withdraw is planned against the carried composition (#2406, closes #2225). - `revl_undo` still re-admits the target generation by a cold start, and now says so: the undo dossier lists in `stateDropped.components` each running component whose live state the rollback drops and the components the target withdraws, with a note, and a forward call whose undo is `revl_undo` carries an `undoNote` that says the same (#2422, closes #2346). - A refusal made while a proposal is held names the held proposal. When the running composition has a name that the held proposal removed, the message lists what the proposal removed and how to proceed, and every such refusal carries the `proposal` block (#2428, closes #2230). ### Placement and interop - Per-component tier placement: a hot component compiles to rust or go while the rest of the composition stays on py or ts, composed over the existing cross-process seam with G2/G3/G4 checked over the whole manifest. - The extern config coeffect: a document-global extern reads static config resolved at plug time instead of shuttling identity through environment variables, on py, ts, go, java, and rust. Config is static data, not a capability channel, and a config field type is restricted to data heads. - Embeddable gate: a two-layer gate API (a pure verdict surface plus a session facade), fail-closed, that never admits what the reference compiler refuses. It ships in three forms. `revl.gate` is the python library facade. `crates/revl-gate` is the native rust library, generated from the self-host sources by `tools/build_gate_crate.py` and committed so it builds with no Python on the machine. `crates/revl-gate-wasm` packages that crate as a WASI-P2 component (`revl:gate@1.0.0`) with an EMPTY import list, so the verdict is a total deterministic function of its arguments, provable from the artifact. Both native forms carry the same asymmetry: they can refuse, and their non-refusing arm is `no-objection`, never an admission. - `crates/revl-lsp`: the language server as a native rust binary (item 336 slices 1 and 2). It answers `definition` and the signature half of `hover` through `revl_gate::symbols` with no interpreter on the path, and forwards the reference front end's diagnostics verbatim, adding native refusals rather than replacing anything. - `revl deploy`: a deploy map and descriptor, attested admission, a coordinated rollback, a deploy-admit runner with a stdio transport, and a cross-machine leg over SSH with a pinned host key (#440, #451, #667, #744, #795, #803, #811). The admission-gated deploy verb is exposed over MCP (#890). - The sandbox isolation ladder: a wasm-cell rung, a microVM rung, a mixed-arch container rung, and a seam transport with per-precondition refusals and an approval channel across the boundary (#638, #678, #437, #511, #621, #610, #817). - Named instances: placement-aware spawn with a named handle, surfaced into the manifest, with a `place` statement and a plan-time gate (#454, #530, #629, #748). Ambient admission for a running composition: provision disjointness, replacement and hand-off state, route and handoff rows on the native gate wire (#599, #645, #1034, #1055, #1107). Composition layers fold a stack into the row table, with per-root admission profiles in `compile_files` (#701, #637). - Attested placement: `requires attested_tee` verifies an enclave against a pinned attestation root, result receipts ride the call path, and a component certificate accounts for the whole guarantee catalogue (#859, #964, #975, #1023, #862, #973). A composition SLO contract with an observed half, a sample floor and a declared denominator runs from inside the running generation (`revl slo`) (#869, #1008, #1024, #1040). - A2A: `import_a2a` binds the HTTP+JSON/REST transport, `through a2a` binds the 1.0.0 wire for the remote row, the four-op Task lifecycle is bound over both entry points, and every generated crossing is correlated with its reply shape-gated and its returns tainted `Untrusted` (#518, #594, #697, #774, #783, #1030, #977, #1084, #630). - The private peer pool: a declared object with a join admission gate, an asymmetric key pair with rotation and revocation, dispatch to a member with a one-result delivery ledger, member liveness (`revl pool probe`, `pool status --require-live`), multi-file dispatch as one bundle, and promotion of a member (#1277, #1290, #1425, #1467, #1541, #2178). - Runtimes and artifacts: `revl bundle` has a one-file form (#490, #585). `revl-lsp` bakes the private runtime into one distributed binary (#435, #738, #802). `revl export client --lang ts` pairs with `revl serve --http` (#450, #554). The Rust gate gained a manifest-aware `admit_into` and an admission surface reachable only behind the refusal gate (#860, #987). Placement runners serve only the keys and methods the spec exports and resolve a key in its provider's realm (#1606, #1620, #1574). Realm labels are per `(realm, key)` (#1549, #2063). - Placement runners: an unconfirmed dispose is no clean teardown on rust, java and ts (#2355). The go runner awaits each dispose in held-mode teardown before it reports DOWN (#2338, closes #2336). An unsupported proxy service is fatal on the rust runner (#2340, closes #2335). ### Security hardening An adversarial review swept every shipped security feature across three waves and found one recurring bug class: an authority-derivation fold that visits one crossing surface and misses an alternate crossing, so a capability is reached but not seen. Every instance was closed. Representative fixes: - Taint (provenance): endorse no longer launders a foreign origin, whether the origin is argument-derived or minted inside the endorsing body. - The untrusted-author admission profile now refuses a host-extern reach through the whole transitive module closure, not only a direct import. - The MCP server no longer treats the driving agent as a trusted host-code author. Source that arrives over the transport compiles under the untrusted-author profile, so an inline `@py` body can no longer be loaded and run. `revl mcp serve --author-trust`, `--provider` and `--grant` are the operator's controls, and the trusted mode stamps every class-(c) ticket with the unreviewed host bodies a yes would also let run. - Path arguments to the MCP verbs are jailed to the operator-sanctioned roots. They were a filesystem oracle over every path on the machine, and a restore plus snapshot round-trip returned an arbitrary file's whole content. - The host-file and host-ref jails gained a realpath-containment gate at every install-origin resolution site (the compiler and the bundle verifier), so a path that escapes the compile root cannot forge a first-party origin. - The approval gate uses one grant-coverage predicate for both auto-approve and revocation, follows the spawn seam as well as the service seam, and installs the session owner on every call. - The atomic compare-and-set guards its inverse on the result across all tiers, so a lost race cannot delete the winner's entry. - A witnessed inverse that reaches an emission through a plain function wrapper is refused, keeping teardown emission-free. - The witnessed-fs workspace jail routes every path family through the guard, not only the ones that already did. An inverse's SOURCE endpoint is confined and restricted to a sidecar the workspace itself produced (a rename removes what it names, so an unchecked source stole outside files rather than merely overwriting them); the sidecar directories are resolved and refused when a symlink is planted under their name; a hardlinked target is refused rather than written through; and every mutation runs through directory fds walked down from the root with `O_NOFOLLOW`, closing the check-to-syscall window. The families are enumerated in the guard and scanned in the `@py` bodies, so a fifth cannot be added silently. - The ts tier's fs host carries the same pass. `backends/typescript/revl_fs_ts.ts` had held every one of those escapes untouched while the py fix shipped: an unconfined inverse source that stole and destroyed outside files, sidecar directories a planted symlink could redirect outside the root, no link-count check, and a name-based write a concurrent writer could divert. It now states the same `PATH_FAMILIES` enumeration, restricts an inverse's source to a sidecar this workspace produced, refuses a symlinked sidecar directory by type, refuses a multiply-linked target, writes only through a verified fd, and refuses a write whose name has parted from the inode that was written. The enumeration is scanned with the TypeScript compiler API, the ts peer of the `@py` body scan. One difference is stated rather than papered over: node exposes no `*at()` syscall, so the check-to-syscall window is narrowed with an `lstat` component walk plus `O_NOFOLLOW` and an fd-identity check, not closed the way the directory-fd walk closes it on py. - The distribution seam refuses a resource crossing on same-tier as well as cross-tier process boundaries, and the wire encoder fails closed on any value that is not scalar, list, dict, or a declared record or ADT. - A network (TCP+mTLS) seam refuses replays. The peer allowlist pinned who may call and deliberately did not dedup, so an admitted peer's request could be captured and re-delivered and the provider dispatched it again. The reason recorded for that (a replay ledger needs the per-boot secret a cross-composition caller can never hold) was wrong: dedup needs an identity to scope the key by, and mutual TLS has already proved one. Freshness is now keyed on the identity the handshake proved, read off the peer certificate and never off the payload a replayer controls, so it needs no shared secret, and a consumer under another conductor is admitted exactly as before. The ledger is bounded per identity, and what an eviction costs is stated rather than hidden. Seams report `peer-bound` where they earned it. - The node bridge seals a correlation envelope. It had no `correlation` parameter at all, so a placement disarmed a provider's guard entirely rather than refuse a caller that could not satisfy it: one node consumer left a py provider unguarded. The ts bridge now seals the same HMAC over the same canonical bytes, proven against the real guard over a real seam. - A boundary policy rule now selects a directly emitted extern by the capability token it declares. `extern emission[db] fn pg_write` put `pg_write` in a component's reach while the register floor, the approval gate and a bound secret all keyed the same crossing `db`, so `capability db requires register keyed` selected nothing on a composition whose only `db` crossing was a direct emission: an operator wrote a floor and silently got less. Reach carries the declared scope now, and the extern's name is no longer a second spelling for the same crossing. `revl audit` renders the scope on the host-code line, and the full table of which spelling a rule selects is in docs/boundary-policy.md. - Approval expiry is monotonic and irreversible. The session clock is anchored to a monotonic source rather than read from the wall clock on every check, and a grant that has been observed expired is latched dead, so nothing that moves the machine's notion of "now" can hand back an expired grant for its remaining uses. - `revl mcp serve --approval-record-values` now defaults to `withheld`: a crossing whose resource target came from a caller argument is not written into the durable cross-session approval log unless the operator opts in with `bound`. The cost of the default is that such approvals no longer fold into a distilled auto-approve rule, and the distiller now says so and names the flag when it declines. Author-written literal targets are recorded either way. - The TypeScript emitter escapes author text that reaches an emitted comment. A `config` field's default is rendered into a `/** default: ... */` doc line, and the string escaping it went through is the one a string LITERAL needs. A comment has no escape syntax at all, only sequences that end it. The doc line now goes through a comment escaper that breaks both of those (a line terminator, and `*/`), so a default can no longer close the comment and put the rest of itself in code position. Emitted string literals, identifiers and template literals were already routed through their own escapers and are unchanged; no golden bytes move. - `revl fmt` proves meaning preservation for every file it writes. Its equivalence gate had two holes that met in the middle: the IR comparison compiled with no module resolution, so every `use`-bearing file skipped it, and the fall-back it landed on compared the formatter's own trivia scanner with itself, which cannot fail no matter how wrong that scanner is. The scanner had drifted from the lexer, so a `use`-bearing file with a triple-quoted string, a `0x`/`0b`/`0o` literal, a `_` digit separator, a `'...'` string, a `\"` escape or a `}` inside a host body was silently rewritten into a different program. The gate now lexes both texts with the reference lexer and refuses on any difference, before anything else and whatever the file contains; the IR comparison resolves imports for an on-disk file so it actually runs; and the scanner calls the lexer's own number, string and brace-matching routines instead of a second copy of its rules. Across the 888 `.rvl` files in the tree the formatter now changes the compiled meaning of none. - Declared secrets are redacted on every tier: the secret registry covers byte secrets, container secrets, Map keys and values, and the escaped rendering of a value, with walks bounded by path instead of recursion. Runner log channels funnel behind confidential redaction (GHSA-4x4q-296m-9x9x) (#391, #432, #894, #905, #911, #912, #916, #918, #920, #923, #924, #925, #926, #931, #835). Bound secrets are injected into extern bodies on ts as on py, and rotate after plug (#1953, #2047). - Effect composition is decided per statement (A1), and emission obligations resolve across a service-typed arrow parameter (#394, #395). The parser and gate bound source and statement nesting so deep input is refused, not crashed, and refuse an unknown top-level IR field or schema revision by name at the admission boundary (#566, #658, #1452, #489, #578, #607). - Host acquisitions undo as their release: an acquisition's undo must be its release on the bound handle, an extern acquire's site undo its declared inverse, and a provide-method host write's undo its inverse with provenance. `no_residue` is no longer green when a bracket's undo did not reverse its write, and session teardown checks that every host resource was released (#1879, #1885, #1961, #1966, #2054, #1869). - The approval gate hashes host code reached as a function value, discloses a declared-untrusted argument on the ticket, carries each capability's crossing ceiling, and treats a witnessed call as class (a) only where its inverse is registered (#1546, #1991, #1819, #2045). A swap ticket names the candidate's host code, and component leases fence load and unload and bound a lease's TTL (#1450, #906). - Taint survives more boundaries: a persisted origin survives the host round trip, the IR keeps a service signature's `Untrusted`/`Trusted` qualifiers, a routed operation's parameters are stamped as inbound request values, and qualifiers are preserved across the manifest boundary (#1993, #1955, #928, #660). Authority does not leak across trust boundaries: an agent-authored successor inherits no operator authority, a proposal does not inherit a standing authority by taking a reviewed component's name, and a replacement's reach is clamped to the inherited bound (#661, #1101, #712). - Seam hardening: a hostile-wire TCK over the sealed seam envelope, a provider failure funneled before it reaches the consumer on every tier and thread, the consumer-side error channel scrubbed, and a wire `$kind` resolved against the declared cases only (#580, #666, #899, #909, #913, #910). - The witnessed fs gained original write receipts with expected-before guards (`revl.fs.write`), identity checks of the preimage and garbage sidecars inside restore and unrm, and an early refusal on hosts without dir-fd support (#529, #606, #1037, #1052, #1070, #2020). - The shell classifier refuses a backslash-newline continuation and a `$` or backtick inside double quotes (#934, #953). Archive extraction in the unload survey is contained, `truc` names are jailed to `trucs/`, and an in-memory compile jails assets and refs lexically (#2124, #846, #1777). Network-exposed signing is refused without `cryptography` (#1534). A model-routed component is accounted for what it can reach, not its token spelling (#2186). - HTTP faces: requests are framed by a single agreed `Content-Length`, a duplicate credential header is visible, and no authority value is decoded from the wire (#901, #914, #1505). ### Operations - E-Stop (item 443): the operator's emergency halt, as a third verdict `halted` alongside `committed` and `aborted`. `revl estop` arms an idempotent latch, `revl estop --report` reads it back, and `revl run --estop-latch FILE` honours it; `revl_estop` and `revl_estop_report` are the MCP forms. The price is stated, not hidden: every registered entry is left stranded and every acquired handle stays held, and `revl recover --wal` is how the books are settled. - `revl changelog`: the release note derived from the structural delta between two compositions, every line traceable to a differ fact (item 261). - `truc`, the component manager (`revl truc`, `src/revl/truc/`): eight components plus a bootstrap that assembles itself through the gate on every invocation. - E-Stop on every tier: ts, node, java, go, rust and wasm report or honour the latch, the py runtime stops every crossing before its host body from any thread, and the ts tier carries an in-flight crossing registry and halt inventory (#453, #514, #611, #773, #779, #769, #1518). - Crash recovery: the WAL stays open for the run's life, a torn tail is sealed before every append, run-complete is scoped to its own interval, recovery requires a settled WAL and records emission outcomes, and a provider's emission made while serving a cross-process call folds into the caller's crossing (#568, #563, #650, #718, #2221, #1890). Two-phase admission has durable stages and forward recovery (#513, #619, #699). Session teardown is awaitable and audited, with original-resource settlement (#533, #656). - The gate/reference census is published as a generated artifact with outsider-recomputable identities and a verifier, and the pinned three-host framework benchmark is published (#1306, #1421, #1469, #2156, #1415, #2154). - `revl doctor` resolves the toolchain reproducibly and reports the revl version through one definition that attestations also stamp (#790, #2267, #2279). `revl mcp serve` re-execs under the runtime venv or announces and refuses the runtime verbs (#1710). `revl run --watch` keeps the ambient provisions across a reload (#1593). `revl run` names the failing lifecycle stage on every error (#768). - Licensing: AGPL-3.0 for the compiler, gate and tooling, MIT for what ships in your program, with a trademark policy (#1237). - Front-door docs for the release candidate (#2415, closes #2409, #2410, #2411, #2412): the quickstart installs from a clone (`pip install -e .`) and no longer points at the unrelated `revl` project on PyPI, the self-host numbers come from docs/conformance.md, the commands and MCP reference documents `revl export openapi`, `slo --seal`, `deploy --bundle` and `import openapi --undo`, and public docs no longer name private downstream projects. - Every `--backend` flag and tier alias map reads one tier list in `src/revl/tiers.py`, so a tier spelled one way is accepted or refused the same way by every command. `revl deploy --backend` is now validated (#2299, closes #2205). - `revl mcp serve --print-config` prints the client config block for the first interpreter that imports both revl and cordis, and `revl doctor` gets a matching row (#2318, closes #2281). - The WAL stops its writers on a failed fsync on go, rust, java and wasm (#2354). Service compensations are journaled, and unsynced records are cut on go, rust and java (#2372, closes #2345). ### Live systems `revl swap`, the runtime why-oracle, and `revl apply` run from a tagged clean checkout as scripted exit tests. ### Known issues Open at this release: the compiler admits a program that the target toolchain then rejects, or that it handles differently from the reference. - #2325 (java): generic variants, `Unit` type arguments and `Set`/`Stream` heads emit invalid Java. - #2328 (self-host rust): a type named `Box` is not renamed to `Box_` as the reference does. - #2344: a `@py` host body reaches the runtime through `sys.modules` although `import runtime` is refused. ### Internal Regeneration, CI, test tooling and roadmap-currency work (the light PR runs and merge-queue CI of #1679, the root-suite sharding, the affected-tests selector, the census and ledger records, the site and playground wheel refreshes, and the main-health fixes between them) is not itemised; it changes no behaviour of the language or its tiers. Recent examples: the env-gate credit guard (#2304), one loader per backend emitter (#2306), a cached test listing for the affected-tests selector (#2307), the pre-merge run of every CI-only suite (#2310), a shared compile of the self-host lowering module across tests (#2314), and a main-red fix to a paths-ignore control (#2330). Later examples: the proof that skip gates are set on the pre-merge path (#2393, closes #2210), the pure-read guard tests in lint (#2365, closes #2333), a selector rule that re-runs the corpus guards (#2357), the test-listing cache in the tools (#2326), and the shared emitter loader for test.py, gate.py and run_wasm (#2323). ### All pull requests in this release Every pull request squash-merged into main from the v2.0.0 point (commit a73dbbfa7, the rewritten form of 04fa2a00b) through origin/main at 631b77a79 is listed here, in ascending order. 106, 114, 149, 154, 157, 199, 201, 205, 206, 208, 209, 212, 214, 215, 216, 219, 220, 221, 222, 227, 229, 230, 231, 232, 235, 243, 246, 251, 265, 273, 288, 289, 292, 302, 306, 308, 321, 333, 334, 351, 366, 376, 378, 379, 380, 382, 386, 391, 394, 395, 396, 398, 400, 409, 411, 412, 413, 451, 453, 454, 456, 459, 460, 462, 472, 473, 474, 481, 484, 487, 488, 489, 490, 491, 492, 493, 494, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 573, 574, 575, 576, 577, 578, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 621, 629, 630, 631, 632, 633, 635, 636, 637, 638, 639, 645, 646, 647, 648, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 666, 667, 672, 674, 675, 676, 678, 679, 680, 682, 684, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 711, 712, 713, 714, 715, 716, 717, 718, 726, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 740, 742, 743, 744, 745, 746, 747, 748, 749, 750, 753, 754, 755, 760, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 798, 800, 802, 803, 804, 806, 807, 808, 809, 810, 811, 816, 817, 818, 828, 829, 832, 833, 834, 835, 836, 837, 838, 839, 840, 842, 845, 846, 847, 848, 849, 850, 851, 852, 853, 855, 856, 857, 859, 860, 861, 862, 863, 864, 865, 866, 869, 870, 871, 874, 875, 877, 878, 879, 881, 882, 883, 884, 886, 888, 889, 890, 891, 892, 894, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 917, 918, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 936, 942, 943, 944, 945, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 958, 959, 960, 961, 962, 963, 964, 965, 966, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 980, 981, 982, 983, 984, 985, 986, 987, 988, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1018, 1019, 1020, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1030, 1031, 1032, 1033, 1034, 1035, 1037, 1039, 1040, 1042, 1043, 1044, 1048, 1049, 1050, 1051, 1052, 1053, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1063, 1064, 1066, 1068, 1069, 1070, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1109, 1110, 1111, 1113, 1114, 1115, 1117, 1118, 1119, 1121, 1122, 1124, 1125, 1126, 1128, 1131, 1133, 1134, 1135, 1137, 1138, 1139, 1140, 1141, 1143, 1144, 1146, 1147, 1149, 1150, 1152, 1154, 1155, 1156, 1159, 1160, 1162, 1163, 1164, 1165, 1168, 1170, 1171, 1173, 1174, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1199, 1202, 1208, 1210, 1213, 1214, 1216, 1218, 1220, 1226, 1227, 1230, 1231, 1232, 1237, 1238, 1239, 1241, 1242, 1243, 1245, 1247, 1250, 1256, 1257, 1258, 1260, 1262, 1264, 1269, 1270, 1271, 1273, 1275, 1277, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1290, 1292, 1294, 1295, 1297, 1301, 1304, 1306, 1312, 1313, 1314, 1317, 1322, 1325, 1326, 1329, 1330, 1335, 1346, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1357, 1360, 1361, 1362, 1363, 1364, 1365, 1372, 1374, 1375, 1378, 1380, 1383, 1384, 1386, 1389, 1390, 1391, 1392, 1395, 1396, 1398, 1401, 1402, 1404, 1405, 1408, 1409, 1411, 1412, 1413, 1414, 1415, 1416, 1417, 1419, 1420, 1421, 1422, 1423, 1424, 1425, 1426, 1428, 1429, 1432, 1436, 1439, 1440, 1441, 1442, 1445, 1447, 1448, 1450, 1452, 1456, 1459, 1466, 1467, 1468, 1469, 1471, 1476, 1478, 1482, 1483, 1485, 1490, 1493, 1495, 1501, 1505, 1514, 1516, 1518, 1523, 1524, 1530, 1531, 1532, 1533, 1534, 1535, 1537, 1538, 1541, 1542, 1544, 1546, 1548, 1549, 1552, 1555, 1556, 1557, 1558, 1563, 1565, 1568, 1570, 1571, 1573, 1574, 1576, 1578, 1579, 1584, 1586, 1590, 1591, 1593, 1594, 1595, 1596, 1597, 1598, 1602, 1604, 1606, 1612, 1615, 1616, 1617, 1618, 1620, 1622, 1623, 1624, 1626, 1628, 1629, 1630, 1635, 1636, 1637, 1638, 1640, 1642, 1645, 1649, 1650, 1651, 1653, 1655, 1657, 1662, 1663, 1665, 1666, 1667, 1668, 1670, 1671, 1673, 1674, 1679, 1687, 1689, 1710, 1711, 1712, 1718, 1719, 1721, 1730, 1731, 1732, 1737, 1741, 1744, 1746, 1748, 1749, 1753, 1758, 1759, 1761, 1763, 1764, 1766, 1767, 1770, 1772, 1773, 1775, 1777, 1778, 1782, 1783, 1785, 1787, 1788, 1789, 1790, 1791, 1794, 1795, 1796, 1797, 1799, 1803, 1804, 1815, 1816, 1819, 1824, 1825, 1827, 1830, 1831, 1833, 1843, 1852, 1853, 1854, 1856, 1858, 1860, 1863, 1865, 1866, 1867, 1868, 1869, 1870, 1871, 1872, 1873, 1874, 1875, 1877, 1878, 1879, 1880, 1882, 1883, 1884, 1885, 1887, 1890, 1891, 1893, 1898, 1906, 1907, 1916, 1917, 1919, 1922, 1925, 1928, 1929, 1930, 1934, 1941, 1943, 1947, 1949, 1951, 1953, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1964, 1966, 1967, 1969, 1970, 1971, 1973, 1974, 1975, 1976, 1977, 1978, 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2010, 2011, 2013, 2015, 2017, 2018, 2020, 2021, 2022, 2023, 2024, 2030, 2039, 2040, 2041, 2043, 2045, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2068, 2069, 2070, 2071, 2072, 2075, 2076, 2078, 2080, 2081, 2082, 2101, 2104, 2106, 2107, 2109, 2112, 2113, 2116, 2119, 2120, 2121, 2122, 2124, 2126, 2128, 2129, 2130, 2133, 2134, 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2148, 2149, 2152, 2154, 2155, 2156, 2157, 2158, 2159, 2161, 2162, 2163, 2164, 2168, 2169, 2170, 2171, 2172, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2182, 2185, 2186, 2220, 2221, 2226, 2227, 2228, 2233, 2235, 2236, 2238, 2242, 2243, 2244, 2245, 2247, 2248, 2249, 2251, 2253, 2255, 2256, 2258, 2261, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2278, 2279, 2282, 2283, 2288, 2290, 2291, 2292, 2293, 2294, 2295, 2298, 2299, 2300, 2301, 2302, 2304, 2305, 2306, 2307, 2309, 2310, 2312, 2314, 2318, 2319, 2320, 2323, 2326, 2327, 2330, 2331, 2338, 2340, 2354, 2355, 2357, 2365, 2369, 2370, 2372, 2375, 2376, 2377, 2387, 2388, 2392, 2393, 2395, 2398, 2400, 2404, 2406, 2415, 2422, 2425, 2426, 2427, 2428 ## 2.0.0 The typed-effect core, the six emitters, and the reference IR. See the roadmap history for the 2.0 line. ## 1.0.0 Initial release.