Agent-driven OpenTelemetry instrumentation
simpletel's bet is that an AI coding agent is a first-class user. This is the recipe as shipped: install the skill, paste one prompt, and let the agent work the ordered steps — with a smoke test before any code change and an exit code, not an eyeball, as the definition of done.
The contract
Three rules hold the whole flow together. The agent never invents a simpletel fact — the
endpoint and the x-simpletel-team header both come from simpletel env,
which is offline and deterministic. Instrumentation is upstream OpenTelemetry only, so there is
no simpletel SDK and no exporter fork. And completion is simpletel verify's zero
exit code, never "traces look like they arrived".
Install the skill
The skill is an embedded asset written into each agent's well-known location, byte-identical across agents and version-stamped from the binary so it cannot drift:
simpletel skill install --agent claude|codex|pi
# Pi users can also: pi install git:github.com/willnewby/simpletel-skill
Claude gets skills/<name>/SKILL.md, Codex gets
prompts/<name>.md plus a managed AGENTS.md block, and Pi gets
the same Agent Skills frontmatter as Claude. The command is idempotent and makes no network
call.
The paste-ready prompt
Human to agent, one paste:
Onboard this repository to simpletel.
If the simpletel onboarding skill is not installed, run `simpletel skill install`.
- Log in: `simpletel login`
- Create a team for this repo: `simpletel team create --name <repo-name>`
- Export the env block: `eval "$(simpletel env)"`
- Smoke-test first: `simpletel run -- <start-command>`, then confirm a `cli.run` trace with `simpletel get traces`
- Instrument this app with the upstream OpenTelemetry for its language (zero-code agent preferred), configured only by those OTel env vars
- Run the app, then poll `simpletel get traces --service <service-name>` until the first trace appears, and report the trace id
No simpletel SDK — upstream OpenTelemetry only.
The ordered steps
The skill walks the agent through: confirm a credential (simpletel whoami, and
ask the human which login path applies if not); ensure a team (simpletel team list,
then team create --name <repo> confirmed with the human); mint the env block
(simpletel env — applied as a shell export for a run, or a 0600 env file /
compose block / secret store for durability); run the smoke test; detect the language and
entrypoint from the build manifest; and instrument with upstream OTel, zero-code agent
preferred. The team id in OTEL_EXPORTER_OTLP_HEADERS is the ingest credential —
the skill forbids committing it or hard-coding it in a tracked file.
Step zero: know the allowance
Before acting, the skill reads the account's plan and usage with
simpletel usage --format json. If the document reports
billing_enabled: false the agent notes it and continues — older and self-hosted
servers are fine, and the rest of onboarding does not depend on billing. If the account is
near or at a limit, the agent says so plainly and surfaces the upgrade_url from
the same document. When a command does fail with a plan-limit rejection, the 429 body carries
an upgrade_url; the agent presents it to the human and stops that step rather
than retrying the same command — raising the plan is the human's decision, not a transient
error to wait out.
The smoke test is not optional
Before any code change, the agent runs a trivial command through simpletel run
and reads it back with simpletel get traces, confirming a recent
cli.run trace. This proves the endpoint, the team header, and network reachability
with zero changes to the app. A red smoke test is a credentials or connectivity problem, and
the agent fixes that first — it never debugs instrumentation against a broken pipe.
The exit-code gate: simpletel verify
Instrumentation "done" is simpletel verify --service <OTEL_SERVICE_NAME>
returning exit 0. It polls until success or the deadline (default 10 minutes;
add --deadline 30s for a slow start) and evaluates three rungs:
| Rung | What it proves | On failure, check |
|---|---|---|
| pipe | any fresh trace arrived at all | endpoint reachability, the x-simpletel-team credential from simpletel env, and that the exporter points at simpletel. simpletel run -- <cmd> is the quick pipe test. |
| identity | a fresh trace under service.name == your OTEL_SERVICE_NAME | that OTEL_SERVICE_NAME was exported with the expected value (the report prints the names it saw), and that the env block was applied to the exporter process, not a parent shell. |
| richness | that trace carries spans from a library auto-instrumentation | auto-instrumentation is not engaged — install the instrumentation packages for the app's dependencies, wrap the real serving process, and exercise real traffic (an idle server emits nothing). Batch exporters flush on a delay (~5s), so wait before concluding it is broken. |
A non-zero exit names the failed rung and the fix; agents and CI branch on the code rather
than parsing output. That is the whole point — cli.run proves the pipe, but only a
rich span proves the app is instrumented. Most onboarding instructions end with "check that
traces show up in the dashboard", which is exactly the check an agent cannot do reliably and
a human does inconsistently; three ordered booleans and an exit status replace it with
something a program can act on and stop at the first real problem.
Framework notes
A zero-code agent comes first wherever one exists: Python
(opentelemetry-instrument), Node
(--require @opentelemetry/auto-instrumentations-node/register), Java
(-javaagent), and .NET's automatic-instrumentation shim all add tracing with no
source edit. Go and Rust have no zero-code agent, so the agent adds the smallest upstream SDK
bootstrap — for Go, the otel + otlptracehttp + sdk/trace
setup reading the OTEL_EXPORTER_OTLP_* env vars — at the entrypoint and flushes it
on exit. Bootstrap and wiring only; manual spans are a follow-up once data flows.
The handoff summary
simpletel verify exiting 0 is the end of the work, not the end of onboarding.
The agent closes by printing four fields, in order: the resolved team id
(20-hex), the service name the app now exports under, the
verification result plus the first verified trace id from its
verified: service=… trace=… line, and the console URL
(simpletel console). Then one line of next-step: open the console's errors-only
view to catch problems early. Never leave the human with just "it worked".