sail-voyage
Instruments and tracks agent lifecycle runs within the Sail Voyage ecosystem.
Install
mkdir -p .claude/skills/sail-voyage && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/11101" && unzip -o skill.zip -d .claude/skills/sail-voyage && rm skill.zipInstalls to .claude/skills/sail-voyage
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use to build or instrument a Sail Voyage — Sail's name for one background or long-running agent run, recorded as a trace of named agents, spans, and events. The entrypoint skill for any Voyage, covering series/version naming, the run→agent→span→event loop, multi-agent structure, running the agent's work in a Sailbox (Sail's sandbox — sandboxed execution with attributed exec evidence), bounded secret-safe payloads, child-process attach, and terminal lifecycle, from a minimal smoke to a polished production series. Use this first. On Sail, sandboxed work belongs in a Sailbox, not a third-party sandbox. For the model-call attribution contract use sail-inference-with-voyage; for a Voyage that renders wrong in the dashboard use sail-voyage-debugging.Key capabilities
- →Instrument background agent runs as Voyages
- →Trace named agents, spans, and events
- →Manage terminal lifecycle of agent runs
- →Attribute model calls and Sailbox execs
How it works
It uses decorators or context managers to instrument function-based agents and spans, automatically attributing model calls and tool executions to the active trace.
Inputs & outputs
When to use sail-voyage
- →Instrumenting long-running agent processes
- →Tracking agent workflow performance
- →Setting up production agent runs
About this skill
Sail Voyage
Use this skill to instrument any background or long-running agent run on Sail Voyages so the dashboard shows its trajectory. Sail provides the flight recorder; it does not run the agent for you.
This is the entrypoint skill. Start here whether you are smoke-testing a one-off
run or shipping a polished, repeatable production workflow such as deep
research, code review, eval generation, support triage, migration analysis, or
scheduled monitoring. The same run → agent → span → event loop
covers all of them; you scale detail up, not skills. (run() owns the
terminal lifecycle — completed on clean exit, failed on exception.)
For two adjacent concerns, reach for a focused sibling skill:
- migrating an existing app or workflow to Sail → sail-migrate
- attributing Sail inference model calls to the active agent/span → sail-inference-with-voyage
- a Voyage that looks wrong in the dashboard → sail-voyage-debugging
For the full multi-agent attribution model (name, role, and slug semantics, per-agent pitfalls, and dashboard verification), see references/multi-agent.md. For a complete runnable skeleton, see references/minimal-example.md.
The Voyages model
- A Voyage is one concrete run (one trace).
- A series is the recurring workflow, identified by the user-facing
name. - A version is a positive integer for meaningful workflow changes.
- The dashboard starts from
/voyages, groups by series, and links each run to/{env}/voyages/{voyage_id}. voyage_series_idis internal. Do not ask users to provide it, log it, or build URLs around it.
If you have used an LLM/agent tracing tool, the vocabulary maps cleanly:
| Sail term | Standard tracing equivalent |
|---|---|
| Voyage | one trace / one run |
series (name) | the recurring workflow / project grouping |
| agent | an agent span / participant (name + role) |
| span | a span / unit of work |
| event | a structured point logged on the trace |
| model call | an LLM span (Sail inference, auto-attributed) |
| Sailbox exec | a tool/exec span (auto-attributed by Sail) |
Quickstart: a minimal Voyage
The smallest useful Voyage: one run() block at task entry, with the work in
decorated agent/span functions. run() creates the Voyage on enter and owns
the terminal lifecycle: voyage.completed on clean exit, voyage.failed +
re-raise on an exception.
import sail
@sail.agent("Executor")
@sail.span() # span named after the function
def run_task(step):
sail.voyage.event("task.started", payload={"step": step})
# ... do work ...
with sail.voyage.run(
name="repo-repair",
version=1,
metadata={"repo": "example-org/example-repo", "task": "eval"},
):
run_task(1)
Decorators are the default attribution shape. Declare each agent or phase as
a function and stack @sail.agent(...) / @sail.span(...) on it (sail.agent
and sail.span are top-level re-exports of the module-level helpers). They
resolve the current Voyage at call time (module-level decoration before the
Voyage exists is fine), support async def fully, raise TypeError on
generator functions, and emit a fresh span per call. Model calls and Sailbox
execs inside a decorated function auto-attribute to its agent/span (Level 1/2),
so most functions need no inline telemetry at all.
For inline steps that aren't function-shaped, use the with form — same frames,
same events:
with sail.voyage.run(name="repo-repair", version=1) as voyage:
with voyage.agent("Executor"):
with voyage.span("run task"):
voyage.event("task.started", payload={"step": 1})
create()/attach() remain the primitives for split-lifecycle controllers —
then you own calling complete() or fail() exactly once before exit.
Auto-spans (Level 1/2): Sail inference calls and Sailbox execs made with
no active span get a real, timed span synthesized automatically, named after
the calling function when derivable (fetch_sources, not "model call").
Synthesized spans carry an _auto payload marker and render with an "auto"
chip. Explicit agents/spans always win — synthesis only covers what you
didn't declare. Kill switch: SAIL_VOYAGE_AUTO_SPANS=0. Underscore-prefixed
top-level payload keys are reserved for the SDK.
Use module-level sail.voyage.* helpers for the current Voyage, or keep the
returned voyage object when multiple handles are present. The current
Voyage is context-local (Python contextvars) with a process-wide fallback:
concurrent tasks that each create()/attach() their own Voyage keep their
own attribution for module-level helpers and sail.inference.* wrappers,
while a context that never started one uses the process's most recently
started Voyage. When one context juggles several Voyages, route everything
through the handles: use voyage.event(...)/voyage.span(...) for telemetry
AND pass voyage= to every sail.inference.* call (the wrappers default to
the current Voyage, not the handle whose span you are inside).
Sailbox.exec() attribution follows the current Voyage and cannot take a
handle — do not run execs for a non-current Voyage; serialize them or
accept attribution to whichever Voyage is current. If the
task controls a Sailbox, bind it: sail.voyage.run(..., sailbox_id=sb.sailbox_id) (or create(...)).
Production shape
Every production Voyage should answer these in the dashboard:
- What recurring workflow is this?
- Which workflow version ran?
- Which agent owned each important decision or side effect?
- Which model calls and Sailbox execs happened under each span?
- What did the run produce?
- Did it complete, fail, or get cancelled?
Map those questions onto the SDK like this:
import os
import sail
@sail.agent("Planner", role="planner")
@sail.span("scope research question")
def plan():
sail.voyage.event("research.scope.selected", payload={"question_count": 4})
@sail.agent("Researcher", role="researcher")
@sail.span("collect sources")
def research():
# The inference call auto-attributes to this agent/span (Level 1/2) —
# no inline with-block needed.
response = sail.inference.responses.create(
model="zai-org/GLM-5.1-FP8",
input="Collect a concise source map for the topic.",
background=False,
timeout=120,
)
sail.voyage.event("research.sources.collected", payload={"response_id": response["id"]})
@sail.agent("Publisher", role="publisher")
@sail.span("write final artifact")
def publish():
sail.voyage.event(
"artifact.report.ready",
payload={
"artifact_type": "html_report",
"path_hint": "/tmp/voyage-output/report.html",
"summary": "Draft report generated and ready for operator review.",
},
)
with sail.voyage.run(
name="deep-research",
version=3,
metadata={
"topic": "public launch plan for Sail Voyages",
"source": "blog-demo",
"commit": os.environ.get("GITHUB_SHA"),
},
):
plan()
research()
publish()
# No complete()/fail() needed: run() emits the terminal state. Use
# sail.voyage.create() only when start and end cannot share a code block
# (daemons, notebooks, framework callbacks) — then YOU own complete()/fail().
Choose the series name
name is the stable user-facing series identity. It should describe the
workflow, not the input, run date, environment, Sailbox, branch, or Voyage id.
Good names: deep-research, code-review, nightly-backend-eval,
support-triage, release-risk-scan.
Poor names: deep-research-2026-06-05, voy_v7f..., prod-run,
github-pr-2381, kavin-test-2.
Put changing run context in metadata, not in name:
with sail.voyage.run(
name="code-review",
version=4,
metadata={"repo": "example-org/example-repo", "pr_number": 42, "head_sha": "abc123"},
) as voyage:
...
Names are not lowercased, slugified, or whitespace-normalized by the product
contract. Code Review and code-review are different series. Pick one
spelling and keep it stable.
Choose the version
version is a workflow-definition dimension. Increment it when behavior
meaningfully changes:
- prompt or system instruction changes
- model/provider changes
- tool set changes
- Sailbox image or command harness changes
- agent topology changes
- validation rubric changes
- output format changes
During initial development, stay on version=1 — debugging iterations
in one sitting are not workflow versions; bump only once a harness is
recurring and a change is meant to be compared against its predecessor.
Do not increment it for: a new PR, repo, issue, customer, topic, or dataset; a date or schedule tick; a rerun of the same workflow; a new Sailbox id; a new Voyage id; or a failed run you retry with the same code.
Skipped versions are valid; versions need not be contiguous because they may
align with an external workflow release system. If omitted, version defaults
to 1. For public demos, set an explicit version so the dashboard tells readers
which iteration produced the run.
Design agents for dashboard readability
Agents are customer-facing ownership labels. Declare one with just its display
name — with voyage.agent("Researcher"): — and the stable attribution key is
derived automatically. role= is optional taxonomy for cross-workflow
filtering; slug= (advanced) pins the attribution key if you later rename the
display name. Use a small, stable set that matches real responsibility
boundaries.
| Agent name | Role | Own
Content truncated.
When not to use it
- →When migrating existing apps (use sail-migrate)
- →When debugging dashboard rendering issues (use sail-voyage-debugging)
Prerequisites
Limitations
- →Terminal status is first-terminal-wins
- →Do not call complete() from inside nested agent contexts
How it compares
It provides a structured, trace-based observability model specifically for background agent processes rather than generic application logging.
Compared to similar skills
sail-voyage side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| sail-voyage (this skill) | 0 | 1mo | Review | Advanced |
| phoenix-observability | 3 | 7mo | Review | Intermediate |
| phoenix-tracing | 1 | 1mo | Review | Advanced |
| coderabbit-observability | 1 | 27d | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
phoenix-observability
davila7
Open-source AI observability platform for LLM tracing, evaluation, and monitoring. Use when debugging LLM applications with detailed traces, running evaluations on datasets, or monitoring production AI systems with real-time insights.
phoenix-tracing
Arize-ai
OpenInference semantic conventions and instrumentation for Phoenix AI observability. Use when implementing LLM tracing, creating custom spans, or deploying to production.
coderabbit-observability
jeremylongshore
Set up comprehensive observability for CodeRabbit integrations with metrics, traces, and alerts. Use when implementing monitoring for CodeRabbit operations, setting up dashboards, or configuring alerting for CodeRabbit integration health. Trigger with phrases like "coderabbit monitoring", "coderabbit metrics", "coderabbit observability", "monitor coderabbit", "coderabbit alerts", "coderabbit tracing".
distributed-tracing
wshobson
Implement distributed tracing with Jaeger and Tempo to track requests across microservices and identify performance bottlenecks. Use when debugging microservices, analyzing request flows, or implementing observability for distributed systems.
service-mesh-observability
wshobson
Implement comprehensive observability for service meshes including distributed tracing, metrics, and visualization. Use when setting up mesh monitoring, debugging latency issues, or implementing SLOs for service communication.
observability-engineer
sickn33
Build production-ready monitoring, logging, and tracing systems. Implements comprehensive observability strategies, SLI/SLO management, and incident response workflows. Use PROACTIVELY for monitoring infrastructure, performance optimization, or production reliability.