Multi-agent
When one logical task involves multiple cooperating agents (a Reviewer, a TestRunner, a GitHub-poster), each agent gets its own context, spans, and attributed work. They all share one Voyage.role= is the optional cohort taxonomy (“reviewer”,
“test_runner”, “source_control”, “executor”) — the dashboard groups runs by
name and offers role as a categorical filter. Pass slug= (advanced) to pin
the attribution key across display renames.
Child-process attach
When the controller spawns subprocesses (e.g., a parallel test runner), the subprocess should attach to the parent’s Voyage rather than create its own. The parent exportsSAIL_VOYAGE_ID; the child calls
sail.voyage.attach(), which reads it:
child_env() returns the handoff env (SAIL_VOYAGE_ID, plus the active
agent context as the child’s SAIL_AGENT_* defaults). It returns {} when
telemetry is disabled, so the same code runs keyless.
Common cross-pattern pitfalls
- Don’t complete the Voyage from inside an agent block. Call
voyage.complete()at the top level, after all agent contexts have exited. - Don’t reuse a Voyage across logical tasks. One Voyage per task; if the agent does N tasks, that’s N Voyages.
- Don’t put secrets in event payloads. Sail has redaction at the SQL ingestion layer, but the safest pattern is to summarize / hash before storing.
- Don’t open more than one Voyage per controller process unless you intentionally have parallel-independent tasks. The SDK has a process- global “current Voyage”; multiple Voyages confuse implicit attribution.
Reference
- Voyages overview
- Voyages quickstart
- Sailboxes
- SDK package:
sail-sdkon PyPI