Skip to main content
Two patterns matter most once you move beyond the quickstart: recording multiple agents in one Voyage and attaching subprocesses to a parent Voyage. Use decorators for function-shaped work. Use a with span only when a temporary block is clearer than a named function.

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, so the dashboard shows one trace for the full background agent workload.
Naming convention: the first (and only required) argument is the display name (“Reviewer”). The stable attribution key is derived from it automatically. 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.

Sailbox exec attribution and .wait()

Sailbox commands run as first-class Voyage evidence when they happen inside a Voyage. Keep the agent context active around the call so the dashboard can show which participant owned the command:
sb.exec(...) returns immediately with a request handle. For foreground commands, call .wait() to observe completion, return code, and output tails. If there is no active span, Sail creates an auto-span for the exec. For foreground commands, that span closes when .wait() observes the result. Use explicit spans for steps you want named in the product. Let auto-spans cover low-level calls when you are migrating an existing harness and only need attribution with minimal code changes.

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 exports SAIL_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

  • Do not complete the Voyage from inside an agent block. Call voyage.complete() at the top level, after all agent contexts have exited.
  • Do not reuse a Voyage across logical tasks. One Voyage per task. If the agent does N tasks, record N Voyages.
  • Do not put secrets in event payloads. Sail applies server-side redaction, but the safest pattern is to summarize or hash sensitive values before recording them.
  • Use explicit handles when one execution context juggles Voyages. Concurrent async tasks can each select their own current Voyage. A raw thread or other context that never selects one uses the process-wide fallback. When one context switches among multiple Voyages, call methods on the intended Voyage object and pass voyage= to inference wrappers instead of relying on implicit attribution.

Reference