ROS2 Markers and Observability¶
The AutonomyOps ROS2 governance stack emits observable PASS markers to stdout at each governance checkpoint. These markers are simultaneously written to a WAL (Write-Ahead Log) for durable audit. CI harnesses, demo scripts, and operators all rely on these exact strings.
PASS marker format¶
PASS <label> context=<context>
label— kebab-case identifier for the checkpoint (stable across versions)context— execution path:container,native, orsim
Example:
PASS ros2-telemetry-active context=container
PASS ros2-runtime-container-ready context=container
PASS ros2-stack-start context=container
PASS ros2-policy-enforced context=container
PASS ros2-wal-recording context=container
Marker reference¶
Marker label |
Context |
When emitted |
|---|---|---|
|
container / native |
WAL emitter initialised — durable recording is live |
|
container only |
Container execution path selected; Docker is reachable |
|
container / native |
About to dispatch to |
|
container / native |
Policy evaluation passed AND execution returned nil |
|
container / native |
WAL has a record of this execution (post-exec) |
Container vs native context¶
ros2-runtime-container-ready is emitted only on the container path. It is
absent when Docker is unavailable and the native fallback is taken.
CI harnesses that assert all five markers must first confirm that Docker is
present. If running on a host without Docker, assert only the four markers
whose context is native:
# Container path — assert all five
grep -F 'PASS ros2-telemetry-active context=container' output.txt
grep -F 'PASS ros2-runtime-container-ready context=container' output.txt
grep -F 'PASS ros2-stack-start context=container' output.txt
grep -F 'PASS ros2-policy-enforced context=container' output.txt
grep -F 'PASS ros2-wal-recording context=container' output.txt
# Native path — assert four (container-ready is absent)
grep -F 'PASS ros2-telemetry-active context=native' output.txt
grep -F 'PASS ros2-stack-start context=native' output.txt
grep -F 'PASS ros2-policy-enforced context=native' output.txt
grep -F 'PASS ros2-wal-recording context=native' output.txt
When markers are NOT emitted¶
Situation |
Markers emitted |
|---|---|
Policy denied ( |
|
|
|
|
|
Execution succeeded |
All five |
ros2-policy-enforced and ros2-wal-recording are post-execution markers —
they are never emitted for denied or failed executions.
WAL recording¶
Every PASS marker is backed by a WAL entry before the stdout line is written. The WAL:
Survives process crashes between marker emission and the final
ros2-wal-recordingIs stored in a temporary directory (
/tmp/adk-ros2-wal-<random>/) for the duration of the governance sessionIs drained to the orchestrator after execution completes
The WAL entry format (JSONL):
{"kind":"PASS","label":"ros2-telemetry-active","context":"container","ts":"2026-04-07T10:00:00.123456Z"}
Querying WAL entries after a run¶
With a running orchestrator:
# Fetch recent log entries from the orchestrator
autonomy logs --orchestrator-url http://localhost:8888 --limit 50
# Filter to this node only
autonomy logs --orchestrator-url http://localhost:8888 \
--node my-edge-01 --limit 50
# Stream new entries as they arrive
autonomy logs --orchestrator-url http://localhost:8888 --follow
WALPass — the marker primitive¶
PASS markers are emitted via markers.WALPass(label, context, emitter) in
cmd/autonomy/pkg/markers/:
// WALPass writes a PASS marker to both the WAL and stdout.
// It calls emitter.EmitPASS first (durable) then markers.Pass (stdout).
// Returns an error if either write fails.
func WALPass(label, context string, emitter PassEmitter) error
The PassEmitter interface is satisfied by *telemetry.Emitter. Tests can
inject a lightweight in-memory emitter to verify marker emission without a
real WAL directory.
CE-tier markers (autonomy run ros2.launch)¶
The CE dispatch path (autonomy run ros2.launch) emits the same five PASS
markers using the same WAL-backed mechanism. The execution context is resolved
by dualpath.Resolve at call time, so context annotation is accurate even when
Docker availability changes between calls.
Observability pipeline¶
PASS marker (stdout)
│
▼
markers.WALPass ──▶ telemetry.Emitter.EmitPASS ──▶ WAL (BoltDB)
│
orchestrator drain
│
GET /v1/logs
│
autonomy logs / SSE
The WAL acts as a durable buffer: even if the orchestrator is temporarily unreachable, markers are persisted locally and drained when connectivity is restored.
Demo script pattern¶
Demo scripts and CI harnesses that need to assert PASS markers can use:
#!/bin/bash
set -euo pipefail
output=$(autonomy ros2 run \
--image ghcr.io/autonomyops/adk-ros2-runtime:local \
launch demo_robot arm_demo.launch.py 2>&1) || true
for marker in \
"PASS ros2-telemetry-active context=container" \
"PASS ros2-runtime-container-ready context=container" \
"PASS ros2-stack-start context=container" \
"PASS ros2-policy-enforced context=container" \
"PASS ros2-wal-recording context=container"
do
if ! echo "$output" | grep -qF "$marker"; then
echo "FAIL: marker not found: $marker" >&2
exit 1
fi
done
echo "All PASS markers present"
Troubleshooting¶
ros2-policy-enforced missing
The governance stack executed but the policy denied the action (or the subprocess returned a non-zero exit code). Check:
The policy file (
demo/bundles/ros2/policies/ros2_safety.rego) allows the requested package and launch file.The
--imagematches the build in the bundleactivation.entrypoint.Run
autonomy bundle inspect demo/bundles/ros2.tar --local --show-policyto verify the policy text.
ros2-runtime-container-ready missing
Docker is not reachable. Confirm:
docker info
If Docker is absent, the native path is taken. The marker is not emitted on the native path — this is expected behaviour, not an error.
WAL entries not appearing in autonomy logs
Requires a running orchestrator:
autonomy-orchestrator serve --listen 0.0.0.0:8888 --data-dir /tmp/orch
autonomy logs --orchestrator-url http://localhost:8888 --follow
See also¶
ROS2 Governance — dual-path execution and policy gates
Robotics Quickstart — end-to-end run
cmd/autonomy/pkg/markers/— marker primitive packagetelemetry/— WAL emitter package