Maintainers publish changes that alter an adopter journey, migration, or relationship; the owning repository remains the authority and links to its evidence. The complete, mechanical stream remains available under releases.
Generated Rust servers refuse a malformed Timestamp and an undeclared JSON member at decode, and the Go server an undeclared top-level member. Authored success acts assert no error. Denied-grant scenarios reach ranked views and every ungranted actor. Generated UIs call their binding's routes.
The interpreted target appends null for an absent optional list element. Binding refusal scenarios compare trigger event identities. A row-set guard beside named rows is witnessed on every refusal order. No-view input refusals read the record back through sends that tell its state apart.
From ess-scenario/6 a claim may name a nested member by its dotted path, and an authored literal input its type refuses is refused at any depth. Suites /62 and /63 carry a response member path and a finite Binary64, compared by IEEE equality in Rust, Go and TypeScript. Synthesis refutes an inequality against an example.
ess generate types takes --http, so each command input is the request body its bound OpenAPI document publishes, and --names short combines with --all-commands and --all-views. Entity Runtime lowering keeps an unstored input type's invariants and prefix or refuses them by name. Synthesis decides an identity guard on the row it arranges.
ess generate types realizes an optional, nullable Binary64 field and refuses a responseless command's response root by name. The schema admits the id: and component: name aliases. Synthesis follows bindings that rewrite a related row, sends refusals beside external branches to a present related row, and picks wrong-state rows with known stored guards.
Synthesized binding scenarios compare the trigger event's identity fields with the instance or row they act on. The arranged half of a refusal without an identity view probes the record through the command. ess specify validate names the file a refusal was read from in a specification of several files.
From ess/33 a struct, map or Json input may carry an example that synthesis sends as written. An entity may declare that the deployment provides its rows; suites resolve such a row as an entity fixture and the interpreted target creates it. Transitions and states may carry a summary.
Schemas carry entity invariants and publish Binary64 bounds, text lengths and distinct lists as keywords. Defined / not defined branches are exhaustive without a default. ess-composition/5 compares events, commands, constraints and variant attributes. HTTP bindings name operation ids and servers.
HTTP routes group alike outcomes and declare own refusals, empty bodies and arrays. OpenAPI import admits open objects; integer formats keep native width. Types keep declared field order and box only recursion. Response fields may share input names. Validate refuses a branch an earlier refusal hides; mutate scores disjoint precedence swaps equivalent.
Synthesis arranges the rows a binding into a per-row set effect needs, keeps a row set followable until a step sets its binding off, witnesses a predicate refusal policy through an arranged row, and arranges an affects row past a creator's predicate guard. A caller model keeps its binding refusal scenarios.
A synthesis seed is admitted for a model whose actors declare caller attributes, and the seed row gives the seeded run its caller. A binding is witnessed through any branch that publishes its trigger, including one that publishes per changed row. A refusal names a state that only a set move reaches.
A set-move refusal names the selected seed rows that synthesis does not apply and points to an authored scenario. Every refusal a set-move-only state causes names that state. A seeded caller-model scenario notes the swapped-caller run it skips. The workspace compiles on its declared rust-version 1.85, and CI checks it.
Synthesis now arranges the related row or the selected rows a binding's invoked command reads through a `when_related:` guard, where the command updates the trigger's row, chooses its branch by a set guard, or shares the trigger's guard. A one-time response's follow-up whose set effect is pinned to a fresh input composes after the disclosure.
Synthesis now arranges the rows of a set update whose creator copies from, or refuses a missing, related row, and rows the update changes where both write one input. `affects:` rows are arranged past an emitting not-found branch, and a binding input filled across a plain newtype conversion arranges its related row.
Format `ess/32` lets one command guard on rows its input names beside a row-set guard, a struct declare `fields: []` and a lifecycle state declare its wire spelling. A view may publish a `Decimal`. `ess verify conform synthesize` no longer stops with InvalidReplay at `ess/26` and later.
The outcome scenario of an external branch that copies an Optional input, on a command guarded by when_related over the row its input names, ran the branch once more with the Optional left out and named an identity no step created, so it failed every conforming target. Its further runs now name the arranged row, as its plain sibling's already did.
0.69.0 arranged the rows a creating branch leaves once per arrangement that asked, so a specification whose related rows reach their state through a move with a stored-field guard synthesized for more than 13 minutes. Those rows are now made once per synthesis, the time is back at 0.68.0's, and the synthesized suites keep the scenarios 0.69.0 added.
Format `ess/31` lets a `sets:` entry store `[]` or append an input element to a stored list, a `List` input carry an `example:`, and a field's `presence:` be `omitted_or_null`. `ess generate types` generates a command's input and response and a view's row. Binding scenarios act on the row their setup arranged, or are refused as ESS-SYNTH-010.
Format `ess/30` drops the `exists: false` branch on a reference every writer checks and nothing deletes. A view's `filter:` may compare a `Timestamp` with `now`, a selector may write `coalesce(field, d)`, a set effect's `where:` may read `state`, a narrowed `external:` race may compensate, and a scenario may pin a generated value.
Format `ess/29` lets a guard write `coalesce(x, d)` on the left of a comparison, so a guard over an `Optional` decides every record, and lets `external:` carry `when_subject:`. A one-time read cell names its reader (suite `ess-conformance/56`). Synthesis witnesses more ranked heads and connective children, and many row-set selectors take seconds.
Format `ess/28` lets a view field and a view filter read the row a reference of the source names, one hop, as it is when the view is read; a value read a referenced row's lifecycle state; a branch require several related rows at once; and a guard refuse a list of references that names a missing row. Suite `ess-conformance/54` witnesses each navigated view.
Binding scenarios arrange the rows a per-row event, a refusal policy or a stored target names; row-set guards are witnessed beside bindings; `affects:` rows are arranged behind a missing-row guard; and one-time disclosure cells reach caller, unknown-instance and wrong-state branches the way the ordinary suite does.
Under report format 2 the generated Go and TypeScript runners end an unsupported required scenario as a skipped subtest or case carrying the target's reason, not a failed assertion. The run still fails once, with one message naming how many required scenarios were unsupported; the report is unchanged.
Format `ess/27` computes a value from an expression in `sets:`, payloads and view fields, reads digest functions in guards, and answers a command's guards in one precedence order, admitting the guard pairs that order settles. `ess verify conform diff` compares two suites, and a suite states the event catalog once.
A conformance suite can hold the instant a target reads as `now` for one command, and synthesis adds a scenario on each `now` boundary a command decides by. Format `ess/26` holds an external refusal beside an existing row to leave it unchanged. Generated Rust decoders enforce a newtype's alphabet and length; the Go and TypeScript explorers draw timestamps.
`ess-history/3` records each operation's input and published events, and `check-history` holds a target to both. Concurrent exploration arranges `external:` causes and replays a failing seed alone. Synthesis arranges period-bounded views, Rust types take raw JSON values, and the one-time response guide counts disclosure cells.
`ess-composition/4` adds `roles:`: one imported component plays another's, and each local command `implements` or `gates` a contract command, checked for input drift by `ess specify compose` ([#525](https://github.com/beyond10x/ess/issues/525)).
`ess/25` adds create-or-update selection by stored fields, trimmed text guards, one event per changed row and refusals ahead of held state. `ess-http/1` binds commands and views to HTTP; `ess generate` projects it to OpenAPI and emits Rust and Go servers. `ess-composition/4` adds roles.
`ess-http/1` binds commands and views to HTTP routes, projected into OpenAPI by `--http` ([#493](https://github.com/beyond10x/ess/issues/493)). `mutate` covers `when_subject:`/`when_related:` guards and authored scenarios ([#515](https://github.com/beyond10x/ess/issues/515)). Go synthesis builds when bounded contexts reference each other in a loop.
An `ess-http/1` route declares its request encoding, each outcome's status, body, headers and redirect, and the standard refusals. `--http` projects them into OpenAPI, and `ess generate synthesize --target rust` or `go` writes a server that serves the binding ([#493](https://github.com/beyond10x/ess/issues/493)).
`ess/25` lets a create-or-update command pick its update with `when_subject:` ([#530](https://github.com/beyond10x/ess/issues/530)), publish an event per changed row, answer an `external:` refusal before the row is read, compare `trimmed:` text, and give a view an `absent:` row. `ess-diff/19` classifies the view change.
`ess-scenario/5` lets an act state its caller under `caller:`, so a command that records `{caller: …}` runs from an authored scenario. Synthesis decides a related guard on a captured struct key ([#521](https://github.com/beyond10x/ess/issues/521)) and writes stored-field guard scenarios no view observes ([#496](https://github.com/beyond10x/ess/issues/496)).
`ess-scenario/5` adds `caller:` to an authored act, so a command that records caller attributes runs from an authored scenario on every target. Synthesis covers `when_subject:` commands whose guarded field no view observes, and picks a related row against the subject's real key.
`ess/24` lets a `kind: struct` type or a command's response declare `undeclared_fields: ignored`, admitting fields it does not declare. `ess generate` writes open objects there, `ess verify diff` classifies opening and closing, and runners admit an undeclared key at exactly those objects. Authored scenarios are judged by the command's precedence plan.
`ess-cli/2` lets a CLI binding omit its config and output globals. `ess verify diff` classifies added and removed domains and rates changes that refuse an existing caller as breaking. Runners check responses of constrained `String` newtypes. Route-escaping wire names and always-true refusals are refused, and synthesis covers more guards.
Validation refuses an accepting `when:` or `external:` branch declared before a branch the held state selects when one request can satisfy both guards (`ESS-COMMAND-004`, naming both branches). Every consumer then answers alike. To fix a refused specification, declare the held-state branch first.
A settled `.ess-output/state.json` no longer records the output's absolute path, device or inode, and regeneration refuses an edited owned file. Generated Rust types turn on arbitrary precision only where they hold JSON numbers. Entity Runtime lowers alphabets and text counts, and concurrent generation under a shared directory no longer refuses as busy.
Suites `ess-conformance/44` and `/45` match repeated event claims as a set in the Rust, Go and TypeScript runners. `ess generate --check` reports drift without writing, `ess specify validate` reports completeness, `ess specify formats` lists every format, and a CLI binding takes a trailing argument list. A membership operand naming a parameter is refused.
A CLI result field may be typed `Json` and is written as JSON rather than JSON text, so a caller parses an answer once. A `{generated: true}` value of type `Optional<T>` is now supplied by the implementation through a generator port in generated Rust and Go, instead of always being absent; a context that implements the port must add the method.
`ess/23` lets an update re-key its record, a value read the state held before the outcome, `deletes:` remove every row a filter selects, an `affects:` entry write one record per list element and an enum declare typed variant attributes. Synthesis witnesses state predicates, struct-identity selectors and row sets beside an addressed record.
Experimental `ess-protospec/1` models communicating peers, bounded channels, timers and safety properties; `ess verify protocol` simulates, replays and explores it. Conformance runners in Rust, Go and TypeScript now order RFC 3339 instants by instant, not by text.
`ess/22` adds fact operands, constant offsets, instant comparison, UTF-8 byte length, distinct list members, typed text operands, row sets with filtered related reads, calendar windows at a fixed offset, binding payload conditions and conditional aggregate measures, each checked by synthesized conformance scenarios in the Rust, Go and TypeScript runners.
ESS adds declared event transports and typed Rust/Go publishers with native timestamps, positional names and constant constructors. Optional recursive Rust types, generated Go behavior, typed view parameters, UI filters and integer bounds complete the update. Breaking interfaces are documented in the release notes.
Generated output committed with its `.ess-output` regenerates in a clone, a second worktree or CI when its owned files still have their recorded bytes, and an unchanged regeneration leaves `state.json` byte-identical; a state carried without matching files refuses, lists them and prints the steps to re-enroll with `ess generate output adopt`.
A generated Rust server looks the input identity up in the storage port before dispatch, so an `existing_instance:` refusal beside a creation and a creating `unknown_instance:` branch beside an update are generated instead of owed; a command whose lookup would not match its creations stays an obligation, and Go, Web and Clap still refuse both forms by name.
An aggregate view whose creating command has a `when_related` guard gets its `<view>/aggregate` scenario instead of ESS-SYNTH-017: each row's related row is arranged, owner-linked guard inputs name an arranged owner, and a group key read from a related row's owner link holds one owner per value.
In a specification that declares actors, generated Rust and Go servers take the authenticated caller and answer `403 not granted` before a command runs for a caller the specification does not grant it; served suites witness the refusal. Existing callers of `dispatch`, `handle` and `serve` must pass the caller.
`ess/20` lets a `when_related` predicate read the related row's held lifecycle state as `state`, and synthesis arranges a related row of an entity already being arranged one level deep; earlier formats refuse the path and keep their suites.
Synthesis now witnesses aggregate keys and values copied from a related row, `when_related` over an `owns` via field, a fresh caller-supplied identity under swapped callers, and a `when:` beside a `when_subject:`; `ess verify diff` leaves no residual for a one-sided declaration.
`ess verify conform report` turns a runner's per-scenario results into an `ess-conformance-report/2` whose producer profile says the results were supplied and ESS executed nothing; aep 0.66.0 records it as evidence.
Every served `501` carries `committed`: false for an unmet obligation, true when the command took effect and delivering what it published failed. Rust shells gain `Refused::Undelivered`.
`ess ui test` runs `ess-ui-test/1` tests, which select nodes by their document path, headless against the terminal renderer, and writes a Playwright spec for the generated React project from the same file.
`ess verify diff` reports an outcome's error `payload:` sources declared, dropped or replaced, and a moved `accepts: nothing`, `returns:` or caller-decided refusal, as their own changes instead of one `unclassified-changed`. The new kinds need `ess-diff/12`; a delta without them keeps its format and bytes.
`ess-ui/1` describes an application's pages, state and live channels without naming a renderer. `ess ui check` reports findings by node path, `ess ui run --tui` runs a document in the terminal, `ess generate ui --target react` writes a React project, and `ess ui docs` renders the format reference from its schema.
The generated Rust and Go servers list the events a command published in its answer, keep request headers, and name and encode every system event. Delivery is tracked per binding, so an undeliverable event no longer blocks later requests, and 501 is declared for a committed command whose delivery failed.
`ess generate synthesize --target rust` generates the command behaviours, view queries and invariant checks the specification fully determines, over storage and context ports you provide. Actor grants arrive as data, the server gains a transport-free `handle`, `--layout crate` writes one crate, and `ess/19` gives an error's fields their sources.
`ess generate synthesize --target go`, `web` and `clap` carry `Json` as `rust` already does: object members keep their order and numbers their spelling, and a clap flag takes one JSON document. No code target refuses a model for using `Json` any more, and a model without it synthesizes the same bytes as before.
`ess generate synthesize --target rust` carries `Json` as the types crate's `json::Value`, unchanged on the wire. The public docs gain generated CLI and diagnostics references, Start here tutorials a test runs against the built binary, task-sized guides and a what-changed page.
`{$instance}` works inside lists, maps, structs and unions (suites `ess-conformance/32` and `/33`); acts claiming an unstated external answer are refused `ESS-AUTHOR-037`. An input refusal may sit beside held-state branches under one precedence order the explorer follows; synthesis meets nested input invariants and witnesses absent fields and stored maps.
Synthesis witnesses a guard comparing a link field with an input and a stored counter at its limit; the first declared of overlapping accepting branches answers; a relation may ride on the identity. `ess-diff/11` names newtype prefix changes, and mutation report and manifest `/3` score unkillable mutants apart from survivors.
`ess/18` adds state-scoped refusals, `state` in a `when_subject` predicate, guards over a related row (`when_related:`) and a binding's delivery context. Synthesis now witnesses map inputs, identity and link view filters, every creating command and the record a refusal needs; mutation scores gained refusals apart from survivors.
The generated Go and TypeScript runtimes run suites `ess-conformance/22` to `/27` with the Rust runner's verdicts, where 0.38.0 refused them. `ess-composition/3` adds `reader: true`, admitting the widenings a consumer may read. Duplicate names are refused once, and the schema publishes name charsets.
`ess verify conform check-history` checks an `ess-history/1` run of several clients for linearizability against the interpreted model and holds each view to its declared consistency; the Go and TypeScript explorers record concurrent histories and inject declared faults. Source format `ess/17` adds `returns: true`, checked as a typed direct response.
Source format `ess/16` adds literal fallbacks, `defined()` over optional aggregates, bodiless requests, related-record values, `now` in guards, create-or-update outcomes, caller attributes, bounded retries, view paging and outcomes over every record a filter selects. `ess-composition/2` checks consumer types.
Source format `ess/15` adds outcome shapes (`deletes:`, `into:`, `unknown_instance:`, `accepts: nothing`, `preconditions:`), `input.` in subject guards, case-insensitive text, optional aggregates, `prefix:`, `Json` and field `presence:`. Synthesis kills more mutants; `ess` can run a pinned release.
Source format `ess/14` adds `{subject: <field>}`, `{increment: <n>}`, `{input: <field>, else: {generated: true}}`, nested struct mappings and `{generated: true}` in `sets:`. Decimal literals are admitted in every format; `subject.<field>` as text is refused. Older readers refuse `ess/14`.
Source format `ess/13` adds `fixture_inputs:` and `ess-scenario/3` adds `fixtures:` with `{$fixture: name}` references, compiled to `ess-conformance/18`/`19`. Rust, Go and TypeScript resolve and type-check the values from an independent provider before the scenario starts. Older readers refuse the new formats; fixture-free models and suites keep their bytes.
Source formats `ess/8`–`ess/12` add `starts_with`/`ends_with`/`contains`, `when_subject: {predicate: …}`, aggregate views with `group_by:`, `alphabet:` and input `example:`, and `outcome_groups:`. `ess verify conform mutate` audits a suite with specification mutants. Older readers refuse each new format; models that use none keep their bytes.
A Kubernetes scan writes `infra-observation/3` and `infra-ir/3`, recording each Secret key as `{"present": true}` instead of a digest that confirms a guessed value; `infra-drift/3` no longer reports a rotated Secret. `ess-observed-bindings/2` acknowledges a mesh proxy or vendor agent by name and reason. Breaking: older readers refuse each new version.
`ess infra import openapi` reads OpenAPI 3.0 by rewriting each schema to its 3.1 form, refusing a construct with no faithful one. `ess verify bindings` reports OBS-BIND-008 for a container or native sidecar in a bound workload that no binding names. Synthesize orders `Timestamp` values by the instant they name.
ess-service-contract extracts an admitted component's service contract, and ess-entity-runtime lowers it into validated Entity Runtime 0.23.0 definitions and typed host binding obligations, refusing what it cannot express by name. Breaking: `ess skill` and the in-repository plugin are removed; the plugin ships from beyond10x/agentplugins.
The ESS agent plugin (ess@ess) now ships from this repository at the binary's version, with skills for writing, retrofitting and conformance-testing a specification. `ess skill` prints the same skills from the binary, and the release gate refuses a plugin version that differs from the workspace.
Source ess/7 declares silent retries of original command results and finite state refusals. Conformance suites 12/13 compare actual retained responses and complete subjects in Rust and Go; unsupported runners refuse. Native APIs preserve typed results, and diff/6 records the new semantics.
Source format ess/6 models input eligibility for external failures and enum subject history. Suite formats 10/11 compare actual before-and-after subject values and assert silent success. Rust, Go and TypeScript runners reject changed, missing or duplicate subjects; older formats refuse the new vocabulary.
A variant is authored as a bare name or as a mapping that also carries `wire`, `display`, `summary` and `code`, so a variant whose wire form is not derivable from its name can be declared at all. Specification format `ess/5`; delta format `ess-diff/5` adds three `TypeChange` cases, so a moved spelling no longer returns an empty delta.
`sets: {field: {cleared: true}}` states absence. Before it, a branch either left the field alone, wrote a literal that synthesis dropped, or wrote an empty string, which is a different reading. The field's type must be `Optional<...>` and the source must be an entity; an event payload is refused because an undetermined field is not the outcome's to set.
`naming: { code: ... }` settles a collision the emitter cannot: a target without dotted type names flattens `X.State` and an authored view `XState` to one identifier. Only code emitters read it, so wire names, document schemas and qualified names are untouched and setting one cannot break a deployed consumer. The source format stays `ess/4`.
`settings:` states each of a component's configuration inputs once, typed from the specification's own `types:`. `ess specify runtime compile` derives the `ess-runtime/1` config and secret slots from it, and refuses a hand-authored slot or two settings binding one environment variable twice. Absence is stated with `Optional<...>` and nothing else.
`when_subject_state` in `ess/3` combines a declared held lifecycle state with input guards, so equal input applied to two different existing states is distinguished. `ess/4` declares error wire names without merging semantic identities and fills emitted event payloads from typed command response fields. New deltas use `ess-diff/3` and `ess-diff/4`.
`delivery: at_most_once` states that a binding is tried once. Loss stays possible and no idempotency obligation is imposed on the handler. OpenAPI requires `Idempotency-Key` only for `at_least_once` commands, conformance records `BindingGap::DeliverySingleAttempt` instead of synthesizing a redelivery, and the format stays `ess/1`.
Opt-in `ess-conformance/5` records selected generated and authored coverage, omitted scenarios, source identities and every refusal occurrence. Report 2 then distinguishes complete passing conformance from empty, unknown or incomplete coverage — a pass over nothing and a pass over everything stop reading the same.
Authored `ess/2` adds `Binary64` fields, distinct from integer and decimal values. Reference, Rust and Go preserve signed zero, subnormals and nearest-even rounding; two typed operands use IEEE equality. Synthesis and conformance refuse unsupported `Binary64` before publication rather than emitting a target that would disagree.
Import complete JSON Schema roots with source identity; generate structural Go, Rust and TypeScript libraries; and run source-pinned normalization recipes. Rust normalization has a library API; Go/TypeScript support is pending. Rust/Web synthesis returns checked failures, and sliced provenance names its digest profile.
`halts_after: <n>` lets an authored scenario require a consumer to stop an ordered scan after n rows. The target reports rows produced and whether the consumer ended the read. Targets without incremental reads report `unsupported`. This adds a claim to `ess-conformance/4`.
`ess verify conform web` emits a browser page that walks authored scenarios, showing actors, state, selected views, and declared consequences. It replays model-determined behavior without executing an implementation, so authors can inspect a specification before filling its obligations.
Authors can declare modelled instances, command timelines, and expectations in `ess-scenario/1`. These scenarios compile into `ess-conformance/2` with identities distinct from synthesized suites. Twenty-seven refusals check their names against the model. Included in the published 0.16.0 release.
Authored scenarios gain `mark:` and `elapsed:` with `not_before`, `within`, and `quiet` bounds. The suite states a duration and its anchor; the target reports an observed or advanced clock. Targets without clock support report `unsupported`. The claims use `ess-conformance/3`.
`reached_by: command_line` and a `cli:` block state that a component's callers are people at a terminal and where each command sits in the tree. `ess generate synthesize --target clap` emits the parser, the handler obligations and the completion scripts from that declaration.
AEP 0.43.0 lets a documentation site publish plans directly — `artifact board --format markdown` renders the page, `artifact graph --format mermaid` renders the diagram, and each column reads its own ladder `descriptions:`. `artifact evidence --from` converts an `ess-conformance-report/1` into evidence using the report's recorded run instant.
AEP 0.42.0 answers the second adopter's report — aep doctor says in six ok/warn/fail lines whether a checkout is ready to be planned in, the epic, story and specification templates seed an Ambiguities section classifying each gap as inferable or requires-stakeholder-input, and the README and site name beyond10x/agentplugins as where the plugins come from.
Agent Plugins 0.4.0 answers the second adopter's report — a copy-paste install block, a recorded golden-path page from feature idea to critiqued plan, a decomposer that files a decision-blocker for every undecided domain relation, four read-only plan critics with a bounded revision loop, and a domain-first guardrail backed by the minimal ess/1 document.
AEP 0.41.0 gives planning artifacts typed external references, provider-level URL patterns, reference filtering, and a read-only reverse-ticket audit so repository plans can be joined to trackers without parsing prose or granting write authority.
Agent Plugins 0.3.7 teaches planning and story migration to record AEP 0.41.0 refs as typed frontmatter, documents the safe already-exists refusal on repeated migration, and corrects the guidance's published version claims.
Agent Plugins 0.3.3 refreshes Workspace Hygiene so agents can recover a finished external legacy tree stranded by stale pre-0.3 relocation intent without bypassing exact source, destination, HEAD, proof, or confirmation gates.
Worktree 0.3.1 lets exact-id reconciliation retire a finished external legacy tree stranded by a stale pre-0.3 relocation intent, while preserving source, destination, HEAD, proof, and confirmation gates.
Identity now advertises its service boundary, local adoption path, API reference, dependencies, and deployment maturity through the shared b10x documentation contract.
The reproducible interaction study now publishes its question, deterministic pipeline, adoption path, and relationship to agent guidance through the shared b10x documentation contract.
Agent Plugins 0.3.0 adds the workspace-hygiene plugin and a Worktree-generated skill for consistent, recoverable checkout lifecycle across supported agents.
AEP 0.40.0 establishes aep as the authored command, retains protocol as an exact compatibility alias, and keeps ESS adaptation at one optional evidence boundary.