SECTOR 03 // REFERENCE

Scenario schema

Every key of the kind Scenario resource and the exact semantics of its five sections.

apiVersion: shinari/v1          # required: recognition marker
kind: Scenario                  # required
name: data-loss/worker-killed   # required
description: <text>             # optional
tags: [slow, network]           # optional: labels for run/list filtering

providers: ...                  # optional: per-scenario overrides, merged over the Project's (later wins)
vars:                           # optional: merged over Project vars
  sleepSecs: 30
timeout: 120                    # optional: whole-scenario deadline in seconds

setup:       [ <step>... ]      # optional
steadyState: [ <step>... ]      # optional
method:      [ <phase>... ]     # optional
verify:      [ <step>... ]      # optional
teardown:    [ <step>... ]      # optional: replaces the default when present

Sections #

sectionrunsfailure consequence
setuponce, firstscenario ERRORED; nothing else runs except teardown
steadyStatetwice: before method (gate) and after it (recovery check)gate failure ⇒ INCONCLUSIVE; recovery failure ⇒ FAILED
methodordered phases, each an ordered steps: listfirst failure stops the timeline ⇒ FAILED
verifyonce, at the end (all steps run even after failures, cumulative)any non-finding failure ⇒ FAILED
teardownalways, even after ERRORED/FAILED; skipped under --keep-up / KEEP_UP=1recorded, never changes the verdict

A phase is:

method:
  - phase: "SIGKILL worker-a; a peer recovers the job"
    steps:
      - run: docker.kill
        with: worker-a

There are no do/check/after buckets inside a phase; kind comes from the verb, and steps interleave acting and observing freely.

Tags #

tags: is a flat list of plain strings. They carry no execution semantics; they exist so run and list can select scenarios with --include-tags / --exclude-tags boolean expressions (see the CLI reference). Each tag must match [A-Za-z0-9_./-]+ so it stays usable inside an expression; validate flags anything else (rule 14).

Teardown default #

When the teardown: key is absent, Shinari runs <lifecycle>.down (the one configured provider implementing up/down). When the key is present (even empty), it replaces that default entirely.

steadyState contract #

Must be idempotent: it runs twice. An active probe (generate load, assert it succeeds) must sample a fresh batch each run. validate warns when a steadyState step resolves to a mutating action (rule 9).

Interpolation #

Each ${...} is a jq expression evaluated over the scope, the same jq used in read:/capture:, so there is one expression language. Every reference is namespaced: the jq input is an object with four engine-owned namespaces, and the first path segment names which one to read from.

namespaceholdsbound by
.vars.NAMEa declared variablea vars: block (project or scenario)
.outputs.NAMEa step result or captureas: NAME or capture: { NAME: ... }
.env.NAMEa declared environment variablethe project’s env: block (see Project & discovery)
.params.NAMEa composed-verb parametera params: list, only inside a kind: Provider body

So ${.vars.job} reads a var, ${.outputs.rsp.value.total} reaches into a captured object, and full jq is available past the first two segments (${.outputs.total.value // 0}, ${.outputs.runs.value | length}). A reference that is the entire value (with: ${.outputs.job}) preserves the result’s type; embedded references stringify, and a jq result of null renders as empty. Captures are scenario-global, ordered, last-write-wins, visible across sections. A reference to a name that no namespace declares is a validate error (rule 10).

A step’s result is captured as an Observation envelope {value, output, meta}: as: rsp binds the whole envelope, so the payload is ${.outputs.rsp.value} and the call’s facts are ${.outputs.rsp.meta.durationMs} / ${.outputs.rsp.meta.status}. read: and capture: operate on the payload. A per-step timeout: (seconds) fails the step if the verb runs longer.

Timeouts #

A per-step timeout: (seconds) bounds one step: the step FAILs and is marked timed-out if the verb runs longer, and its context is cancelled. A top-level timeout: bounds the whole timeline (setup through verify): if it expires the scenario is FAILED with scenario exceeded timeout <N>s. Teardown still runs after a scenario timeout. The http provider honors whichever deadline applies for any duration; with no step or scenario deadline it falls back to a 30s per-request default.