AOS Hub / Docs

Properties, observations, and verdicts

Fault evidence answers “what physical/model action occurred?” Properties answer “did the system meet its requirement?” A useful Crucible experiment normally asserts both. A dropped frame is not proof that a client failed, and a client timeout is not proof which fault caused it.

#Assertion structure

[properties] has a generated content ID and zero or more assertions. Each [[properties.assertion]] has a stable id, a failure message, and one temporal property. IDs are referenced by property terminal conditions, savepoint selectors, assertion_state predicates, findings, and replay.

Unknown kinds and fields are rejected. Property state and observation history are checkpointed, so resume does not forget a prior witness or restart a deadline.

#Choose a temporal property

KindMeaningUse it for
alwaysPredicate holds at every relevant evaluation pointSafety invariants: no split brain, no invalid state, no unexpected crash.
sometimesPredicate becomes true at least onceA witness is required but no trigger-relative deadline is needed.
eventuallyAfter trigger, nested property holds within deadline_ticksBounded recovery, failover, delivery, or durability.
after_quiescencePredicate is checked once at quiescence or the run limitStable final state and convergence checks.
reachablePredicate should be reachable or unreachableCoverage expectations and forbidden states.

reachable with expectation reachable uses on_unreached = "warn" by default or "fail" when absence is a test failure. Expectation unreachable fails as soon as a witness appears.

An eventually deadline begins at the exact trigger observation coordinate. Use it instead of a global “sometime before run end” check for recovery SLOs. Always pair liveness properties with a finite CLI time/quantum budget.

#Predicate catalog

Predicates are structured kind tables or one of the named DSL strings.

PredicateTrue whenRequired inputs
atVirtual time equals a coordinateat_ticks
afterA duration elapsed since an event last firedduration_nanos, event of
timerNamed relative timer firesname
network_matchA delivered frame matchesnested frame predicate, optional link
console_matchCaptured serial output matchesnode, deterministic regex
coverage_pointGuest executes an address/symbolnode, nested point
memory_predicateSampled register/memory satisfies unsigned comparisonnode, place, comparison, value
io_patternSelected modeled I/O occursnode, I/O kind
node_stateNode has selected lifecycle statenode, state
assertion_stateAnother assertion is satisfied/violatedname, state
quiescentNo immediately runnable scheduler work remainsnone
namedRegistered DSL predicate resolves truename, optional nodes
guest_markerWhite-box guest emits the declared markermarker
all_ofEvery child is truepredicate array
any_ofAt least one child is truepredicate array
onceChild has ever become truepredicate
notChild is falsepredicate

Named strings are no_crashed_nodes, quiescent, node_alive:<node>, and node_crashed:<node>. Prefer structured predicates when the claim needs parameters or will be consumed by tooling.

Nested values are closed too:

  • network frame match is any, exact bytes, contains bytes, or prefix bytes;
  • coverage point is a guest address or resolvable symbol;
  • memory place is physical address, virtual address, symbol, or register, with width u8, u16, u32, or u64;
  • unsigned comparison is eq, ne, lt, le, gt, or ge;
  • I/O kind is any, block read, block write, fsync, 9p, or network; and
  • node state is started, crashed, hung, or exited.

#Observation boundaries

Crucible evaluates predicates only from deterministic observation sources.

ObservationWhat it provesImportant boundary
Network adapterFrame admission, route, mutation, drop, or delivery to QEMUDoes not prove application parsing or acceptance.
Storage adapterRequest, result, bytes, cache and durable frontierCompletion status and actual durability are separate.
Node/QEMU adapterLifecycle generation, instruction/register/memory/interrupt/clock/device actionMust be acknowledged by the matched QEMU capability.
ConsoleStable guest serial bytesRegex over deterministic captured stream; avoid timestamps/random text.
Guest markerExplicit white-box semantic eventRequires the declared guest assertion/marker protocol.
CoverageAddress/symbol executionReachability, not semantic success.

Use adapter evidence for the modeled cause and guest evidence for the application consequence. For example, a recovery property can trigger on an availability transition and require a guest “service-ready” marker by a deadline.

#Evaluation lifecycle

Assertions move through declared lifecycle state and retain witnesses, trigger coordinates, deadlines, and violation evidence. assertion_state allows one assertion or event graph condition to depend on another without re-evaluating its predicate. Cycles and invalid references are rejected.

Terminal behavior depends on the command:

  • run --until property stops on the selected property boundary;
  • a violated assertion produces the property-failure status class;
  • save --at property --property <id> exports the exact violation boundary;
  • search --on-violation stop stops at the first finding, while collect continues within budget; and
  • replay requires the same observation and assertion evolution.

A timeout is distinct from a violated property. Treat exit 1 (counterexample) and exit 2 (budget timeout) differently in CI.

#Evidence chain

For a signal-driven fault, the canonical explanation chain is:

signal coordinate and input digest
  -> binding mapping and selector
  -> typed opportunity and phase
  -> composed effect request
  -> capability acknowledgement and adapter result
  -> observation
  -> predicate transition
  -> assertion/verdict

The trace may omit unchanged sample payloads under the binding observability policy, but stable identities and digests still allow locked replay to verify the chain. Search findings add the schedule and exact fault-mutation recipe.

#Authoring patterns

#Bounded recovery

Use the physical fault transition or a guest “fault observed” marker as the eventually.trigger, then nest the application-ready predicate with an exact deadline. Also assert always for invariants that may not be violated during recovery.

#Expected isolation

Assert network drop/availability evidence separately from a guest marker that proves protected data was not accepted. A frame predicate alone cannot grade the application guarantee.

#Durability under power loss

Assert the storage durable frontier or selected cache-loss evidence, then use a post-restart guest marker or memory/file observation for recovered application state. A successful flush response is insufficient when testing a lying flush.

#Forbidden hardware state

Use reachable with unreachable for a precise forbidden observation, plus a positive coverage witness proving the relevant code path was exercised. This avoids passing only because the test never reached the fault site.

#Review checklist

  1. Each requirement uses the temporal kind that matches its quantification.
  2. Liveness has both a trigger-relative deadline and an outer run budget.
  3. Physical/model evidence and application evidence are asserted separately.
  4. Console regexes and markers are stable across replay.
  5. Reachability tests include a positive path-coverage witness.
  6. CI distinguishes violation, timeout, backend failure, and invalid input.
  7. Failure artifacts retain the trace and exact terminal checkpoint.

See the schema reference, Artifacts and replay, and Debugging for the corresponding configuration and investigation workflows.