Repository navigation
ESS 0.57.0
Added
ess-cli/2: a CLI presentation binding may leave out theconfigandoutputglobals;state
is still required.ess specify clicompiles it toess-cli-plan/2, which leaves out an
undeclared global's key and never writesnull.ess generate clidefines no flag for an
omitted global, and a CLI withoutoutputwrites JSON only. Aness-cli/1binding still
requires all three globals and compiles to the sameess-cli-plan/1bytes as before
(#481).ess-diff/15:ess verify diffnames a domain added to or removed fromsystem.yamlas
domain/<name>/added(compatible) ordomain/<name>/removed(breaking for callers and
readers), where it reportedsystem/<name>/unclassified-changed. A purely additive revision now
passes--fail-on breaking-or-unknown, andess verify impactreaches only the domain's own
constructs. A delta carrying a domain change is written asess-diff/15; earlier formats refuse
it (#469).ess-conformance/46(/47with coverage): a command response that reaches aStringnewtype
withalphabet:,prefix:or value invariants keeps its success scenarios, where synthesis
refused them asESS-SYNTH-001. The scenarios carry the type's rules as constraints, and the
native, Go and TypeScript runners fail a returned value that breaks one (ESS-CF-PAYLOAD). A
record invariant or a reading on a response type stays refused by name. Suites without a
constrained response keep their bytes and format
(#499).
Changed
-
ess-diff/16:ess verify diffrates a change that moves a command input's wire form between a
value, an object and an array as breaking for callers, where it rated it unknown and
--fail-on breakingpassed it. That covers an input field retyped (Stringto a record, text to
ListorMap, and back) and a same-name type redefined so a use in an input moves (aString
newtype redefined as a struct, a struct member retyped to a list). The delta records the shapes
before and after; earlier formats refuse it. A move within one wire form,Jsonon either side,
and a type with no input use keep their answer, format and bytes. -
ess-diff/17:ess verify diffrates three changes that refuse an existing caller as breaking
for callers, where it rated them unknown and--fail-on breakingpassed them: an added input
whose type is notOptional, an added refusal, and a refusal whosewhen:guard changed so it
now refuses an input the earlier revision accepted. An input the earlier revision already
refused, by any refusal under any name, does not count, and neither does an enum value it did
not declare. The two refusals are decided only where every outcome of the earlier revision, and
each up to the refusal in the later one, is a plainwhen:orotherwisebranch the mutation
audit's satisfiability check decides; anything else stays unknown. The delta
recordsnarrows(required-input,refusal-added,refusal-widened) beside the answer;
earlier formats refuse it. An added accepting outcome, an addedOptionalinput and every other
delta keep their answer, format and bytes (#514). -
ess specify validaterefuses a domain, command or view wire name that contains/, or is.
or.., aspath_segment_wire_name(ESS-DOMAIN-012,ESS-COMMAND-012,ESS-VIEW-012), naming
the declaration and the wire name. Such a name was copied into every generated HTTP path as it
was, so it could address another route. Views are refused whether or not a network component
serves them. Dots inside a name stay accepted. -
Validation refuses a refusal whose
when:always holds (when: truebesideerror:), as
ESS-COMMAND-004naming the branch, with the hint to give it the condition it refuses on or to
dropwhen:and declare it as the default refusal. Such a refusal had no one step in the
precedence order: beside another refusal that held, the model interpreter answered the other
refusal, while Entity Runtime and the Rust and Go targets answered this one. An accepting
when: truebranch is unchanged (#489). -
In
ess-cli/1andess-cli/2,null,~and""as a global's flag are refused with an error
naming the global; before, they were read as a flag callednull
(#481).
Fixed
ess verify conform synthesizekeeps theexternal:and success scenarios of a command that
has an input no guard reads whose newtype invariant refuses the default witness, beside several
string guards on another input. They were refused withESS-SYNTH-003("no candidate of the 4
tried"). The candidate search now starts that input at the first value its invariant admits,
instead of varying it last and dropping every candidate before it. That value is the one the
search reached first before, so suites the search already served keep their scenarios
(#511).ess verify conform run --target interpreteddecides a command with a current-time (now)
guard, where it answered every scenario of that commandunsupported, authored ones included.
The interpreter decides at the instant of the step being executed, which is also the instant
that step'snow_offsetvalues resolve against. It reads that instant once per decision.
The interpreted run reads no machine clock: its wall is the fixed
now_offset::earliest_run()instant, so two runs print the same report. The text report now
prints the diagnostic under anunsupportedscenario, as it already did under anerrorone,
so a guard the target cannot decide is named. The library addsRunner::command_clock, a
command clock that reads the runner's step instant
(#510).ess verify conform mutatescores a guard mutant on a branch carrying bothwhen:and
when_subject:asequivalent(ESS-MUTATE-005) where its mutatedwhen:can hold only with an
input field absent that thewhen_subject:predicate needs to compare: such a comparison is
Unknownon every row, neverTrue, so the branch is selected on no row and no scenario can
take it.when: defined(expected_version)besidewhen_subject: version != input.expected_version,
negated tonot defined(expected_version), was reportedunwitnessed(its suite refused the
branch withESS-SYNTH-003). The entry'sunsatisfiable_guardnames the mutatedwhen:and the
stored predicate together. A stored predicate that can still hold, through a test reading no
absent input or through another disjunct, keeps its scoring, and a failed scenario still kills
the mutant.ess verify conform mutatescores aprecedence-swapof two branches that answer alike as
equivalent(ESS-MUTATE-005), where it reported a survivor no implementation could kill:
the same error, the same or no payload, and neither branch changing state, setting or emitting
anything. The entry names the shared answer asidentical_answer, apart from
unsatisfiable_guard, which still means only that the two guards never overlap. A failed
scenario still kills such a mutant. Two refusals that differ in error, payload or effect keep
their scoring. A report or manifest carryingidentical_answeris the new
ess-mutation-report/5oress-mutation-manifest/5, and one carrying none keeps its earlier
version and bytes; an earlier manifest carrying it,/4included, or one carrying it on a mutant
that is not aprecedence-swap, is refused
(#517).- Synthesis writes the
wrong_statescenario of a command whosewhen_subject:sibling
quantifies over an input list and compares its elements with the row, such as
not (exists audience in input.aud: audience == {fact: client_id}). The candidate search drew
that list from the input guards alone, so no candidate held the row's value and the scenario
was refused asESS-SYNTH-003, whatever the row. Where no candidate is found, the search now
tries lists grounded on the row's values. Each sibling is still refuted through any one of its
conjuncts (when:orwhen_subject:). A scenario where every row and input leave some sibling
with all its conjuncts holding is still refused withESS-SYNTH-003. Suites the first search
already served keep their bytes (#516). - The doc comment on the generated Rust
Generated<P>bundle namesTryContextand its legacy
Contextblanket adapter only where the same synthesis emits them. Where no generated behaviour
asks the context anything (for example when every command with anexternal:outcome is owed),
it named two traits the crate does not have. Modules that emit them keep their bytes; no type,
trait or signature changes. - The Go and TypeScript runners that
ess verify conform synthesize --target go|typescript
generates admit a suite whose shapes holdjson. A model with aJsonfield synthesized such a
suite, and both runners refused it at admission withsuite admission: <Command>/outcome/<name>: unknown primitive. Ajsonvalue is compared structurally, as the Rust runner compares it. - The test suites remove every scratch directory they create under
TMPDIRwhen its guard
drops, panics and read-only fixtures included; directories a process-lifetime cache holds live
underCARGO_TARGET_TMPDIR. Aness-xtaskcheck runs a set of suites with an emptyTMPDIRand
names any prefix left behind. - Synthesis no longer overflows the stack on a command with two identity guards over a
self-reference. A search for a row beside the arranged entity is cut only where it re-enters
itself, and the nearest Optional reference is left out before synthesis refuses; models that
synthesized before produce the same suites (#474). - A refusal whose guard reads only the input id gets a witness again: the stored-row half of the
scenario is searched with that id fixed, and is dropped when no stored row meets the guard,
instead of failingESS-SYNTH-019(#479). - Arranged and steered creations take distinct identities per distinction, and boundary rows no
longer re-send the first identity. A scenario that would still create one literal id twice with
no delete between is withdrawn asESS-SYNTH-001naming it
(#480, #471). ess verify conform synthesize --component <c> --scenarios <dir>lists an authored scenario
that drives a command, event or view the component does not realise asoutside:, as it does a
generated one, where it filed it in the component's suite and the scenario could only fail. The
summary counts only the authored scenarios the suite keeps
(#513).- The text conformance report prints
<check code>: <cause>under each scenario that ended in
error; the JSON report is unchanged (#471). - Synthesis witnesses every
any:andall:of an input guard once per child at any depth, not
only at the top level, so theguard-connectivemutant of anall:nested in anany:(or the
reverse, or one undernot:) is killed. A per-child input none of the guard's own candidates
provides is solved for directly, so anany:of four three-wayall:s is witnessed at every
child; one no search finds is anESS-SYNTH-022refusal beside the scenario that names it,
rather than a child skipped silently. A child that cannot decide the connective alone is not
refused: its text literals contradict the input it would need (starts_with "AB"beside
starts_with "A", which the longer prefix implies).ess verify conform mutatedecidesdefinedtests of
Optionalinput fields, so aprecedence-swapof two branches no input selects together, such
asdefined(x)andnot defined(x), isequivalentrather thansurvived. Suites for guards
with only top-level connectives are unchanged (#501).