motel: Checks Files for User-Defined Invariants¶
2026-06-13T15:01:55Z by Showboat 0.6.1
motel check can load user-defined structural thresholds from a separate YAML checks file. This keeps invariants outside the topology, which is useful when the topology is generated by motel import or shared across environments with different limits.
A topology and checks file¶
The topology has one gateway root. It calls auth.verify and orders.list, and orders.list calls database.query. That gives a static depth of 2, direct fan-out of 2, and 4 spans per trace.
cat > /tmp/checks-file-topology.yaml << 'EOF'
version: 1
services:
gateway:
operations:
request:
duration: 10ms
calls:
- auth.verify
- orders.list
auth:
operations:
verify:
duration: 5ms
orders:
operations:
list:
duration: 8ms
calls:
- database.query
database:
operations:
query:
duration: 4ms
traffic:
rate: 10/s
EOF
echo 'wrote /tmp/checks-file-topology.yaml'
wrote /tmp/checks-file-topology.yaml
cat > /tmp/checks-pass.yaml << 'EOF'
version: 1
checks:
max_depth: 2
max_fan_out: 2
max_spans: 4
p99_depth: 2
p95_spans: 4
EOF
echo 'wrote /tmp/checks-pass.yaml'
wrote /tmp/checks-pass.yaml
build/motel check --checks /tmp/checks-pass.yaml --samples 20 --seed 42 /tmp/checks-file-topology.yaml
PASS max-depth: 2 (limit: 2)
path: gateway.request → orders.list → database.query
p50: 2 p95: 2 p99: 2 max: 2 (20 samples)
PASS max-fan-out: 2 (limit: 2)
worst: gateway.request
p50: 2 p95: 2 p99: 2 max: 2 (20 samples)
PASS max-spans: 4 static worst-case, 4 observed/20 samples (limit: 4)
p50: 4 p95: 4 p99: 4 max: 4 (20 samples)
PASS p99-depth: 2 (limit: 2)
p50: 2 p95: 2 p99: 2 max: 2 (20 samples)
PASS p95-spans: 4 (limit: 4)
p50: 4 p95: 4 p99: 4 max: 4 (20 samples)
The checks file adds result rows for percentile assertions while also replacing the default static limits. The command still reports the built-in max-depth, max-fan-out, and max-spans checks, but their limits come from the checks file.
Failing a user-defined invariant¶
Tightening max_depth to 1 makes the topology fail, even though the topology itself is valid. The process exits non-zero, so the pattern works in CI.
cat > /tmp/checks-tight.yaml << 'EOF'
version: 1
checks:
max_depth: 1
max_fan_out: 2
max_spans: 4
EOF
echo 'wrote /tmp/checks-tight.yaml'
wrote /tmp/checks-tight.yaml
build/motel check --checks /tmp/checks-tight.yaml --samples 0 /tmp/checks-file-topology.yaml 2>&1; echo "exit code: $?"
FAIL max-depth: 2 (limit: 1)
path: gateway.request → orders.list → database.query
PASS max-fan-out: 2 (limit: 2)
worst: gateway.request
PASS max-spans: 4 static worst-case (limit: 4)
Error: one or more checks failed
exit code: 1
Overriding file limits at the command line¶
Explicit limit flags override matching values from the checks file. This lets CI use a shared checks file while allowing a one-off local experiment or gradual threshold adjustment.
build/motel check --checks /tmp/checks-tight.yaml --max-depth 2 --samples 0 /tmp/checks-file-topology.yaml
PASS max-depth: 2 (limit: 2)
path: gateway.request → orders.list → database.query
PASS max-fan-out: 2 (limit: 2)
worst: gateway.request
PASS max-spans: 4 static worst-case (limit: 4)
Percentile thresholds need sampling¶
Percentile assertions such as p95_spans and p99_depth are evaluated from sampled traces. If --samples is 0, motel check rejects the checks file before running the topology analysis because there is no distribution to compare.
cat > /tmp/checks-percentile.yaml << 'EOF'
version: 1
checks:
p99_depth: 2
EOF
echo 'wrote /tmp/checks-percentile.yaml'
wrote /tmp/checks-percentile.yaml
build/motel check --checks /tmp/checks-percentile.yaml --samples 0 /tmp/checks-file-topology.yaml 2>&1; echo "exit code: $?"
Error: percentile checks require --samples greater than 0
exit code: 1
build/motel check --checks /tmp/checks-percentile.yaml --samples 20 --seed 42 /tmp/checks-file-topology.yaml
PASS max-depth: 2 (limit: 10)
path: gateway.request → orders.list → database.query
p50: 2 p95: 2 p99: 2 max: 2 (20 samples)
PASS max-fan-out: 2 (limit: 100)
worst: gateway.request
p50: 2 p95: 2 p99: 2 max: 2 (20 samples)
PASS max-spans: 4 static worst-case, 4 observed/20 samples (limit: 10000)
p50: 4 p95: 4 p99: 4 max: 4 (20 samples)
PASS p99-depth: 2 (limit: 2)
p50: 2 p95: 2 p99: 2 max: 2 (20 samples)